Revision 337 (by dpavlin, 2004/06/10 19:22:40) new trunk for webpac v2
/*
	this file composed by Klaus Ripke from the iAPI files by Robert Janusz.
	this is the test app "ix.c" with all the includes slurped in,
	see // #include "isomething.c"

	you may run this with:
	cp iAPI.txt iAPI.c
	gcc iAPI.c
	./a.out <dbpath>
*/

/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The Example of ISIS (UNIX v.3) Database Handle
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// Linux gcc Compiler conventions !

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

// Global variables +++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static
  char DBpath [71];
  char UpCase [256];

#define NFIND 3 // let's keep it for future

#define PACKED __attribute__((packed)) // it shoud work on Linux gcc ;-)

// #include "idef.c"
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The Type Definitions for Unix-ISIS databases 
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// gcc Linux Compiler conventions !
// *** ONLY UNIX ISIS = the difference between DOS/UNIX structures

// *************************** TYPE DEFINITIONS *****************************

// Cross Reference File XRF structure +++++++++++++++++++++++++++++++++++++++

typedef /* Packed */ struct   // /* Packed */ and order are necessary for block reading
  {
    long int xpos PACKED;
                                 // Block nr: 1, 2...; < 0: last block
    long int xrec [127] PACKED; 
                                 // References to Master File records...
          // xrec [...] = 2048 *xrmfb [21 bit] & xrmfp [11 bit]
          // rec. deleted log.     < 0          &   > 0 (restore: -xrmfb)
          // rec. deleted phis.    =-1          &   = 0 (ref@MST.ctrl)
          // rec. does not exist   = 0          &   = 0
          // new rec. to invert                    bit 1024 = 1
          // rec. edited to inv. (add & del post.) bit  512 = 1
  }
xrfBlock;

// Master File (MST) record structures... +++++++++++++++++++++++++++++++++++

typedef /* Packed */ struct   // The first record in Master File
  {
    long int  ctlmfn PACKED   // Always 0
            , nxtmfn PACKED   // Next Master File Number
            , nxtmfb PACKED;  // Next block in Master File, the 1st has 1
    short int nxtmfp PACKED   // Next position in the last block
            , mftype PACKED;  // 0 for user's bases; 1 for messages
    long int  reccnt PACKED   // Reserved for future
            , mfcxx1 PACKED   // Reserved for future
            , mfcxx2 PACKED   // LAN, > 0: inversion, update not possible
            , mfcxx3 PACKED;  // LAN, = 1: exists exclusive user
  }
mstControl;

typedef /* Packed */ struct  // Fixed part of record header that must be...
  {                          // included in the same block; 
                             // the header can begin only on 0, 2, 4...498
    long  int mfn   PACKED;  // MFN number
    short int mfrl  PACKED;  // Length of record (allways eaven)...
                             // LAN: < 0 ==> record blocked
    long  int mfbwb PACKED;  // Block Pointer     |  If the record waits for inv...
    short int mfbwp PACKED;  // Position Pointer  |  ...ref. to old location
                             // mfbwb, mfbwp:  = 0 at creation;  
                             // after modif. = ref to the old record.
                             // xrmfp bit 1024 = 1 at creation 
                             // (= waits to be inverted);
                             // xrmfp bit  512 = 1 at edit 
			     // (=waits to be inv. & delete old references)
  }
mstHdrFix;

typedef /* Packed */ struct
   {
   short int base   PACKED  // Position of data fields in the record
           , nvf    PACKED  // Number of fields in the record
           , status PACKED; // 0 = active, 1 = deleted logicaly (- xrfb < 0)
   }
mstHdrTxt;

typedef /* Packed */ struct
  {
  mstHdrFix mhf PACKED;     // This header is allways in MST file block
  mstHdrTxt mht PACKED;
  }
RecHeaders;

typedef /* Packed */ struct // Index for every field in the record
  {                         // dimension = 6 *nvf; base = 18 + 6 *nvf
    short int tag PACKED    // Label of the field
            , pos PACKED    // Position of the first char in data part: 0, 1...
            , len PACKED;   // Length of the field
  }
mstIndex;

// Inverted File structures +++++++++++++++++++++++++++++++++++++++++++++++++

// Control File (CNT) structure..............................................

typedef /* Packed */ struct
  {
  short int idtype   PACKED  // B*tree type; 1 = N01/L01, 2 = N02/L02
          , ordn     PACKED  // = 5; a half of size of key-record in N0x
          , ordf     PACKED  // = 5; a half of size of key-record in L0x
          , n        PACKED  // = 15; number of buffers for nodes
          , k        PACKED  // = 5; number of buffers for 1st index level (k < n)
          , lev      PACKED; // Current number of levels in index; -1: no N0x
  long int  posrx    PACKED  // Reference to the root in N0x
          , nmaxpos  PACKED  // Next record in N0x
          , fmaxpos  PACKED; // Next record in L0x
  short int abnormal PACKED; // 0: N0x has only the root
  }
cntBlock;

// Index of Dictionary (N0x) structures......................................

typedef /* Packed */ struct
  {
  long  int pos PACKED; // Number of record
  short int ock PACKED  // Number of active keys in the record: 1, 2, 3...2*ordn
          , it  PACKED; // B*tree type
  }
nodeHeader;

typedef /* Packed */ struct
  {
  char key [10] PACKED;
  long int ref  PACKED;  // > 0: reference to next node;
                         // < 0: ref. to leaf: idx [1st].key = key
                         // = 0: inactive
  }
nodeIndex1;

typedef /* Packed */ struct
  {
  char key [30] PACKED;
  long int ref  PACKED;
  }
nodeIndex2;

typedef /* Packed */ struct
  {
  nodeHeader hdr      PACKED;
  nodeIndex1 idx [10] PACKED; // Vector of 2*ordn elements
  }
nodeBlock1;

typedef /* Packed */ struct
  {
  nodeHeader hdr      PACKED;
  nodeIndex2 idx [10] PACKED; // Vector of 2*ordn elements
  }
nodeBlock2;

typedef /* Packed */ union
  {
  nodeBlock1 nb1 PACKED;
  nodeBlock2 nb2 PACKED;
  }
nodeBlock;

// Leaf File (L0x) structures (all index terms)..............................

typedef /* Packed */ struct
  {
  long  int pos PACKED; // Number of leaf record: 1, 2...
  short int ock PACKED  // Number of active keys in the record: 1..2*ordf
          , it  PACKED; // = 1 for L01; = 2 for L02
  long  int ps  PACKED; // Next record in the order: key [ock] < ps^ idx [1st].key
  }
leafHeader;

typedef /* Packed */ struct
  {
  char key [10]  PACKED;
  short int xxx  PACKED; // *** ONLY UNIX ISIS; 
  long int infb  PACKED  // Reference to IFP segment *** != ISIS.MANUAL ***
         , infp  PACKED; 
  }
leafKey1;

typedef /* Packed */ struct
  {
  char key [30]  PACKED;
  short int xxx  PACKED; // *** ONLY UNIX ISIS;
  long int infb  PACKED  // Reference to IFP segment *** != ISIS.MANUAL ***
         , infp  PACKED;
  }
leafKey2;

typedef /* Packed */ struct
  {
  leafHeader hdr      PACKED;
  leafKey1   idx [10] PACKED; // 2*ordf keys
  }
leafBlock1;

typedef /* Packed */ struct
  {
  leafHeader hdr      PACKED;
  leafKey2   idx [10] PACKED; // 2*ordf keys
  }
leafBlock2;

typedef /* Packed */ union
  {
  leafBlock1 lb1 PACKED;
  leafBlock2 lb2 PACKED;
  }
leafBlock;

// Inverted File Posting structures..........................................
// ordained postings organized in segments: Index --> Master File

typedef /* Packed */ struct
  {
  long int ifblk       PACKED  // Number of block
         , ifrec [127] PACKED; // Vector of long int *** != ISIS.MANUAL ***
                               // In 1st block ifrec [1st/2nd] = next pos. in IFP
  }
ifpBlock;

typedef /* Packed */ struct  // This header and 1st posting - always in IFP block
  {
  long int ifpnxtb PACKED  // Pointer to the next segment: 1, 2... | = 0: last seg.
         , ifpnxtp PACKED  // 0, 1...                              | = 0: last seg.
         , ifptotp PACKED  // Tot. number of postings (o.k. only in the 1st segment)
                           // = Sum (segment=1, nseg; ifpsegp)
         , ifpsegp PACKED  // Number of postings in this segment
         , ifpsegc PACKED; // Max. number of postings in this segment (<= 32768)
  }
ifpHeader;

// ifpHeader & 1st ifpPosting always in the same ifpBlock
// Postings in Segment are always in chain of IFP blocks

typedef /* Packed */ struct // Stored left-->right + 0 --> 8 char
  {
  long  int pmfn PACKED; // First 3 bytes --> Master File
  short int ptag PACKED; // Field ident - according to File Selection Table (FST)
  char      pocc PACKED; // Number of occurence of the field
  short int pcnt PACKED; // Counter of the termin inside the field
  }
ifpPosting;

typedef struct
  {
  int        opened;     // Which files are opened (bit spec.)...
  FILE       *mst;       //  1
  FILE       *xrf;       //  2
  FILE       *ifp;       //  4
  FILE       *n01;       //  8
  FILE       *l01;       // 16
  FILE       *n02;       // 32
  FILE       *l02;       // 64
  mstControl mctrl;      // MST ctrl record
  cntBlock   cnt1, cnt2; // Inv. Files ctrl records
  long int   ibm, ipm;   // IFP new Segment Bock/Pos
  char       name [72];  // Database name
  }
BaseCtrl;

typedef struct  // This structer must be allways corelated with BaseCtrl
  {
  char      st [31]; // Max. string to find
  short int v;       // Field to find; 0: all fields
  short int it;      // Type of B*Tree <== strlen (st)
  leafBlock lbl;     // Block L0x with index terms
  short int ilk;     // Position of term in lbl...idx
  char      *term;   // The term in lbl...idx
  long  int ib, ip;  // Number of block/position for IFP
  ifpBlock  ibl;     // Block IFP with posting/s
  ifpHeader ihd;     // Header for Segment of postings
  long  int iiseg;   // Number of posting in current Segment (index)
  long  int iitot;   // Number of posting in total (index)
  long  int iall;    // Number of posting in all Segments
  char      *ipst;   // Ref. to last posting string [8 chars]
  cntBlock  *cntr;   // BaseCtrl.cntr1/2 <== it
  FILE      *n0x;    // N01 or N02       <== it, = BaseCtrl.n0x
  FILE      *l0x;    // L01 or L02       <== it, = BaseCtrl.l0x
  }
TableQuery;

#define RECLENGTH 8192

typedef union
  {
  char       buf [RECLENGTH];
  RecHeaders hdrs;
  }
RecCtrl;

// *** MAIN DATABASE FILE RELATIONS (MST <--> XRF) ***
//      1      2      3
// MST |-mblk-|-mblk-|-mblk-|-mblk-|-mblk-|-mblk-|-mblk-|-mblk-|...
//       ctrl                  |----record----|
//                        |--->|
//      1      2      3   |     pointer: B/P (P in [0, 2, 4... 498])
// XRF |-xblk-|-xblk-|-xblk-|...
// MFN=  1...   128... 255...
// Record                   |-->-pos->--|
//       |-mhf-mht-|-idx[1]...idx[nvf]-|---text...---|
//       |-------------- record buffer --------------|

// *** INVERTED FILE RELATIONS (N0x <--> L0x <-->IFP) ***
// N0x |-nbl-|-nbl-|-nbl-|-nbl-|-nbl-|-nbl-|...
//      -->-root->-|  |-->--next-->--|   |->-->-| r<0
//                                              |
// L0x |--lbl--|--lbl--|--lbl--|--lbl--|--lbl--|--lbl--|...
//                                                   |
//                  |-<--------------<-------------<-| r<0
// *** POSTING ***  |
// IFP |---ibl---|---ibl---|---ibl---|---ibl---|---ibl---|---ibl---|...
//       |next      |-ihd-|---Segment---|         |-ihd-|---Segment---|
//       |Seg.         |-->------>------>-next->--|      |||...
//                                                       MFN...
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// #include "idef.c"

// Let's specify non int header ;-) just to keep order

void ShowRec (RecCtrl *rc);
void setQuery (BaseCtrl *bc, TableQuery *tq, char *sx, short int fld);
void isis ();

// #include "iutil.c"
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The UTIL procedures for Unix-ISIS databases 
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// gcc Linux Compiler conventions !
// *** ONLY UNIX ISIS = the difference between DOS/UNIX structures

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int ReadMem (void *ptr, FILE *f, long int nr, long int size)
  { // Read "nr"'s block of size "size" from file "f" into "ptr"...
  if (fseek (f, nr * size, SEEK_SET) != 0) return (-1);
  if (fread (ptr, size, 1, f) == 0) return (-2);
  return (0); // O.K.
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void doUpcase (char *s, char *st, int slen)
  { int i;
  for (i = 0; i < slen; i++)
    { if (i < strlen (st)) s [i] = UpCase [(int) st [i]]; else s [i] = ' '; }
  s [slen] = 0;
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int sComp (char *s1, char *s2, int len)
  { int i;
  for (i = 0; i < len; i++)
    { if (s1 [i] > s2 [i]) return (1);  // .gt.
      if (s1 [i] < s2 [i]) return (-1); // .lt.
    }
  return (0); // .eq.
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int bfOpen (FILE **f, BaseCtrl *bc, char *ext, char *opt)
  { char st [81];
  if (strlen (bc->name) + strlen (ext) > 80) return (-2);
  strcpy (st, bc->name); strcat (st, ext);
  if ((*f = fopen (st, opt)) == NULL) return (-1);
  return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int InitBase (BaseCtrl *bc)
  // Error return: -1 base file/s; -2 inv. file/s; -3 mst. ctrl rec/s;
  //               -4 cnt param/s; -5 ifp param/s
  { ifpBlock ib;
  bc->opened = 0;
  if (bfOpen (&bc->mst, bc, ".MST", "rb") != 0) return (-1);
  bc->opened += 1; // MST opened
  if (bfOpen (&bc->xrf, bc, ".XRF", "rb") != 0) return (-1);
  bc->opened += 2; // XRF opened
  if (bfOpen (&bc->ifp, bc, ".CNT", "rb") != 0) return (-2);
  if (fread (&bc->cnt1, sizeof (cntBlock), 1, bc->ifp) == 0 ||
      fread (&bc->cnt2, sizeof (cntBlock), 1, bc->ifp) == 0) return (-2);
  if (fclose (bc->ifp) != 0) return (-3);
  if (bfOpen (&bc->ifp, bc, ".IFP", "rb") != 0) return (-2);
  bc->opened += 4; // IFP opened
  if (bfOpen (&bc->n01, bc, ".N01", "rb") != 0) return (-2);
  bc->opened += 8; // N01 opened
  if (bfOpen (&bc->l01, bc, ".L01", "rb") != 0) return (-2);
  bc->opened += 16; // L01 opened
  if (bfOpen (&bc->n02, bc, ".N02", "rb") != 0) return (-2);
  bc->opened += 32; // N02 opened
  if (bfOpen (&bc->l02, bc, ".L02", "rb") != 0) return (-2);
  bc->opened += 64; // L02 opened
  if (fread (&bc->mctrl, sizeof (mstControl), 1, bc->mst) == 0) return (-3);
  if (bc->cnt1.ordn != 5 || bc->cnt1.ordf != 5 || bc->cnt1.n != 15 ||
      bc->cnt1.k != 5  || bc->cnt2.ordn != 5 || bc->cnt2.ordf != 5 ||
      bc->cnt2.n != 15 || bc->cnt2.k != 5) return (-4);
  if (fread (&ib, sizeof (ifpBlock), 1, bc->ifp) == 0) return (-5);
  if (ib.ifblk != 1) return (-5);
  bc->ibm = ib.ifrec [0]; bc->ipm = ib.ifrec [1];
  return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void Done (BaseCtrl *bc)  // Close the database files
  {
  if (bc->opened &&  1) fclose (bc->mst);
  if (bc->opened &&  2) fclose (bc->xrf);
  if (bc->opened &&  4) fclose (bc->ifp);
  if (bc->opened &&  8) fclose (bc->n01);
  if (bc->opened && 16) fclose (bc->l01);
  if (bc->opened && 32) fclose (bc->n02);
  if (bc->opened && 64) fclose (bc->l02);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

char UpMe (char c) // ^A = ^a etc.
  {
  if (c > 96) c = c - 32;
  return (c);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void outS (char *st)
  {  char i;
   if (st == NULL) return;
   for (i = 0; i < strlen (st); i++)
//    c = tblconvert [st [i]];
   if (st [i] == (char) 145) printf ("<");        // This is HTML <
   else if (st [i] == (char) 146) printf (">");   // and that is >
     else printf ("%c", st [i]);
   }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
		    
void outC (char ch)
  { char st [2];
   st [0] = ch; st [1] = 0; outS (st);
  }
  
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  
void outD (double x, int n)
  { char s [80];
  gcvt (x, n, s);
  outS (s);
  }
				  
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void outN (long int x) // long int -> sting @ base=10
  {
//  char st [33];
//  ltoa (x, st, 10); outS (st);
    printf ("%li", x); 
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


// #include "imasterf.c"
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The Master File access to Unix-ISIS databases 
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// gcc Linux Compiler conventions !
// *** ONLY UNIX ISIS = the difference between DOS/UNIX structures

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int GetRec (BaseCtrl *bc, long int mfnr, RecCtrl *rc, int bufvol)
  { char      mblk [512]; // Master File block;
    long int  xmfb, xmfp; // Pointer to Master File (last read) record
              // xMfB < 0 : record deleted (status = 1), -xMfB --> block;
              //            xMfP = 0: deleted phisicaly
              //      = 0 &      = 0: does not exist
              // 1st block: xMfB = 1, xMfP = 0; pointer = xMfB * 2048 + xMfP
              // ==> mst < 500 MBy;
    long int xn, xb;
    int      ret, nch;
    xrfBlock xblk;
    mstHdrFix *hf = &rc->hdrs.mhf;
    mstHdrTxt *ht = &rc->hdrs.mht;
    char      *b  = rc->buf;

  if ((mfnr < 1) || (mfnr >= bc->mctrl.nxtmfn)) return (-2); // No record

  // Find the address in XRF...
  xn = mfnr - 1; xb = xn / 127;
  if (ReadMem (&xblk, bc->xrf, xb, sizeof (xrfBlock)) != 0) return (-1);
  xn = xblk.xrec [xn % 127]; // = address in MST
  xmfb = xn / 2048; // Master File Block
  xmfp = xn % 512;  // Master File Position

  // Find the record in MST...
  if (xmfb < 1) return (1); // Record deleted
  ret = 0; // Record o.k.
  if (ReadMem (&mblk, bc->mst, xmfb - 1, sizeof (mblk)) != 0) return (-1);

  // Copy headers into record buffer...
  memcpy (b, &mblk [xmfp], sizeof (mstHdrFix));
  if (hf->mfn != mfnr) return (-2);    // Database corrupted
  if (hf->mfrl >= bufvol) return (-3); // Buffer too small

  // Get the rest of the record
  xmfp += sizeof (mstHdrFix); // Posinion in block
  xb = sizeof (mstHdrFix);    // Position in buf
  xn = labs (hf->mfrl) - xb;   // Number of chars to read
  if (xn <= 0) return (4);    // No fields ??
  for ( ; ; )  // We have some chars
    { // Find number of chars to copy from this block
    if (xn <= (nch = sizeof (mblk) - xmfp)) nch = xn;
    memcpy (b + xb, &mblk [xmfp], nch);
    xb += nch; xn -= nch; xmfp = 0;
    if (xn <= 0) break; // Nothing more to read
    if (fread (&mblk, sizeof (mblk), 1, bc->mst) == 0) return (-1);
    }

  if (ht->status != 0) ret = 1; // Record deleted logicaly
  if (hf->mfbwb != 0 || hf->mfbwp != 0) ret = 2; // Record not inv.
  if (hf->mfrl < 0) ret = 3; // Record blocked
  return (ret);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int IsF (RecCtrl *rc, short int v, char ch, char *st)
  { short     int n, k, kk, flag;
    char      *b   = rc->buf;
    mstHdrTxt *mht = &rc->hdrs.mht;
    mstIndex  *ind = (mstIndex *) &rc->buf [sizeof (RecHeaders)];

  flag = 0;
  for (n = 0; n < mht->nvf; n++)
    {
    if (ind->tag == v) // there is a tag
      {
      if (ch == ' ') // field found
        { flag = 1; break; }
      else // there is sub-field
        { flag = 1;
        kk = 0; k = mht->base + ind->pos;
        while (b [k] != '^' || b [k + 1] != ch)  // convention ;-)
          { k += 1; kk += 1;
          if (kk >= ind->len) // out of field
            return (0);
          }
        break;
        }
      }
    ind += 1; // next index ref.
    }
  if (flag == 0) return (0);  // false
  if (st == NULL) return (1); // true
  else
    if (sComp (&b [mht->base + ind->pos], st, ind->len) == 0)
      return (1);
    else return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void RepF (RecCtrl *rc, char nf, ...)

  // Repeated "nf" fields in record "rc", the left label appears only if:
  // - begins with "\n", - there was another field and there is defined
  // filed; the right label appears only if ther was defined field.

  { va_list   pinfo; // for ... parameter list
    int       i, c, flag, n, k, kk, v;
    int       hit = 0;
    char      *sl, *sr;
    char      *b   = rc->buf;
    mstHdrTxt *mht = &rc->hdrs.mht;
    mstIndex  *ind = (mstIndex *) &rc->buf [sizeof (RecHeaders)];

  for (n = 0; n < mht->nvf; n++)
    {
    va_start (pinfo, nf);
    for (i = 0; i < nf; i++)
      {
      kk = 0;
      k  = mht->base + ind->pos;
      sl = va_arg (pinfo, char *);    // left
      v  = va_arg (pinfo, short int); // field
      c  = va_arg (pinfo, char);      // sub-field
      c = UpMe (c);
      sr = va_arg (pinfo, char *);    // right
      if (v != ind->tag) // another Tag
        break;
      if (c != ' ') // sub-field
        { flag = 1;
        while (b [k] != '^' || UpMe (b [k + 1]) != c) // convention ;-)
          { k += 1; kk += 1;
          if (kk >= ind->len)
            { flag = 0; break; }
          }
        if (flag) { k += 2; kk += 2; }
        }
      else flag = 1;
      if (flag)
        { // Reference to NULL = NW error !!!
        if (sl == NULL) flag = 0;
        else if (*sl == '\n') flag = 1; else flag = 0;
        if (hit != 0 || flag) outS (sl);
        hit += 1;
        if (c == ' ')
          while (kk < ind->len)
            {
            if (b [k] != '<' && b [k] != '>') // HTML special ;-)
              outC (b [k]);
            k += 1; kk += 1;
            }
        else
          while (kk < ind->len && b [k] != '^') // convention ;-)
            {
            if (b [k] != '<' && b [k] != '>') // HTML special ;-)
              outC (b [k]);
            k += 1; kk += 1;
            }
        outS (sr);
        }
      }
    va_end (pinfo);
    ind += 1;
    }
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// #include "iinvterm.c"
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The Inv. File Terms access for Unix-ISIS databases 
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// gcc Linux Compiler conventions !
// *** ONLY UNIX ISIS = the difference between DOS/UNIX structures

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int FindFirst (TableQuery *tq)
  {
    short int  i, j  // Indexes for idx []
            ,  srec, klen, sidx;  // Size (rec), key length, size (idx)
    long  int  r;    // Reference (record number); < 0: --> L0x --> IFP
    char       *ref;
    leafHeader *lhdr = &tq->lbl.lb1.hdr;   // = ...lb2.hdr
    nodeBlock  nbl;
    nodeHeader *nhdr = &nbl.nb1.hdr;       // = ...nb2.hdr

  if (tq->it == 1)
    { klen = 10; srec = sizeof (nodeBlock1); sidx = sizeof (nodeIndex1); }
  else
    { klen = 30; srec = sizeof (nodeBlock2); sidx = sizeof (nodeIndex2); }

  r = tq->cntr->posrx;  // Root in N0x
  while (r > 0L)
    {
    if (ReadMem (&nbl, tq->n0x, r - 1, srec) != 0) return (-1);
    j = nhdr->ock;
    if (nhdr->pos != r || nhdr->it != tq->it ||
       j < 1 || j > 10) return (-3);  // 10 = 2*ordn
    ref = (char *) &nbl.nb1.idx; // Reference to first elem. of index
    ref += sidx; // The first key is allways <= st, skip it
    for (i = 1; i < j; i++)
      if (sComp (ref, tq->st, klen) > 0) break;
      else ref += sidx;
    ref -= sidx; // Correct value
    r = *(long int *) (ref + klen);  // We have the "ref" value
    if (r > tq->cntr->nmaxpos) return (-3); // Out of N0x
    }

  // Now find the term in L0x index
  if (tq->it == 1)
    { srec = sizeof (leafBlock1); sidx = sizeof(leafKey1); }
  else
    { srec = sizeof(leafBlock2); sidx = sizeof(leafKey2); }

  r = - r;
  if (ReadMem (&tq->lbl, tq->l0x, r - 1, srec) != 0) return (-1);
  j = lhdr->ock;
  if (lhdr->pos != r || lhdr->it != tq->it ||
     j < 1 || j > 10) return (-2);  // 10 = 2*ordf

  tq->ilk = 0;
  ref = (char *) &tq->lbl.lb1.idx; // Reference to first elem. of index
  ref += sidx; // The first key is allways <= s, skip it
  for (i = 1; i < j; i++)
    if (sComp (ref, tq->st, klen) > 0) break;
    else { ref += sidx; tq->ilk += 1; }

  ref -= sidx;
  tq->term = ref; // We have the term address
  if (tq->it == 1)
    ref += 12;    // += 10; *** NOT UNIX
  else ref += 32; // += 30; *** NOT UNIX
  tq->ib = *(long int *) ref;  // = infb
  ref += sizeof (long int);
  tq->ip = *(long int *) ref;  // = infp
  return (0);     // O.K.
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int FindNext (TableQuery *tq)
  { int        srec;  // Block size
    long  int  r;     // Reference (record number); < 0: --> L0x --> IFP
    leafHeader *lhdr = &tq->lbl.lb1.hdr;   // = ...lb2.hdr

  if (tq->it == 1) srec = sizeof (leafBlock1);
  else srec = sizeof (leafBlock2);

  tq->ilk += 1; // Next key in the index
  if (tq->ilk >= lhdr->ock) // Out of index
    { r = lhdr->ps; // Next L0x record in order
    if (r <= 0)
      { tq->ilk -= 1; return (1); } // Nothing more, end of index
    if (ReadMem (&tq->lbl, tq->l0x, r - 1, srec) != 0) return (-1);
    if (lhdr->pos != r || lhdr->it != tq->it ||
       lhdr->ock < 1 || lhdr->ock > 10) return (-2);  // 10 = 2*ordf
    tq->ilk = 0;
    }

  srec = tq->ilk;
  if (tq->it == 1)
    { tq->term = tq->lbl.lb1.idx [srec].key;
      tq->ib = tq->lbl.lb1.idx [srec].infb;
      tq->ip = tq->lbl.lb1.idx [srec].infp;
    }
  else
    { tq->term = tq->lbl.lb2.idx [srec].key;
      tq->ib = tq->lbl.lb2.idx [srec].infb;
      tq->ip = tq->lbl.lb2.idx [srec].infp;
    }
  return (0); // We have the new key
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int Find (BaseCtrl *bc, TableQuery tabq [], int ifind, long int *npg
         , RecCtrl *rc, long int recvol)
  { int i;

  if (ifind <= 0 || ifind > NFIND) return (-1);
  for (i = 0; i < ifind; i++)
    if (FindFirst (&tabq [i]) < 0) return (-1);

  i = Postings (bc, tabq, ifind, npg, rc, recvol);
  return (i);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int IndexPrep (BaseCtrl *bc, TableQuery *tq1, TableQuery *tq2, char *st)
  { char sx [31];             // tqx->v contains the status of L0x index
    int  i;

  if (st == NULL)
    for (i = 0; i < 30; i++) sx [i] = ' ';
  else
    for (i = 0; i < 30; i++)
      if (i < strlen (st)) sx [i] = st [i]; else sx [i] = ' ';

  sx [30] = 0;
  setQuery (bc, tq2, sx, 0);  // We have a long term in tq2; v := 0
  if (FindFirst (tq2) < 0) return (-1);

  sx [10] = 0;
  setQuery (bc, tq1, sx, 0);  // We have a short term in tq1; v := 0
  if (FindFirst (tq1) < 0) return (-1);

  while (sComp (tq1->term, tq1->st, 10) < 0)
    if ((tq1->v = FindNext (tq1)) != 0) break;  // At the end or error
  if (tq1->v < 0) return (-1);

  while (sComp (tq2->term, tq2->st, 30) < 0)
    if ((tq2->v = FindNext (tq2)) != 0) break;  // At the end or error
  if (tq2->v < 0) return (-1);

  return (IndexTest (tq1, tq2));
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int IndexTest (TableQuery *tq1, TableQuery *tq2)
  {
  if (tq1->v == 1)    // At the end of 1st
    if (tq2->v == 1)  // At the end of 2nd
      return (0);     // All done
    else return (2);  // We have only 2nd
  else
    if (tq2->v == 1) return (1);  // We have only 1st
    else if (sComp (tq1->term, tq2->term, 10) <= 0) return (1);
         else return (2);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int IndexTerm (int i, TableQuery *tq1, TableQuery *tq2)
  {
  if (i == 1) tq1->v = FindNext (tq1);      // The term was in 1st
  else if (i == 2) tq2->v = FindNext (tq2); // The term was in 2nd
       else return (-1);
  return (IndexTest (tq1, tq2));
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void setQuery (BaseCtrl *bc, TableQuery *tq, char *sx, short int fld)
  { // Must be activated AFTER the initialization of database

  tq->v = fld;
  if (strlen (sx) <= 10)
    { tq->it = 1; doUpcase (tq->st, sx, 10);
      tq->cntr = &bc->cnt1; tq->n0x = bc->n01; tq->l0x = bc->l01;
    }
  else
    { tq->it = 2; doUpcase (tq->st, sx, 30);
      tq->cntr = &bc->cnt2; tq->n0x = bc->n02; tq->l0x = bc->l02;
    }
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


// #include "ipost.c"
/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
iAPI software: The Inv. File Postings access for Unix-ISIS databases 
Copyright (C) 2000 by Robert Janusz
E-mail: 	rj@jezuici.krakow.pl
Address:	Robert Janusz, ul. Kopernika 26, 31-501 Krakow, Poland
		tel: (0048-12) 4294416/432 ;   fax: (0048-12) 4295003.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You 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.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */

// gcc Linux Compiler conventions !
// *** ONLY UNIX ISIS = the difference between DOS/UNIX structures

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int PostSeg (BaseCtrl *bc, TableQuery *tq)
  {
  if (ReadMem (&tq->ibl, bc->ifp, tq->ib - 1, sizeof (ifpBlock)) != 0)
    return (-1);
  if (labs (tq->ibl.ifblk) - tq->ib != 0) return (-2);
  memcpy (&tq->ihd, &tq->ibl.ifrec [tq->ip], sizeof (ifpHeader));
  tq->ip += 3; // = long int.s / (header - posting) ***!= ISIS.MANUAL ***
  tq->iiseg = 0;
  return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int PostNext (BaseCtrl *bc, TableQuery *tq)
  {
  if (tq->iitot >= tq->iall) return (1);  // No more postings
  if (tq->iiseg >= tq->ihd.ifpsegp) // No more postings in this seg.
    { // Get next Segment...
    tq->ib = tq->ihd.ifpnxtb; tq->ip = tq->ihd.ifpnxtp;
    if (PostSeg (bc, tq) != 0) return (-2);
    }
  tq->iiseg += 1; tq->iitot += 1;
  tq->ip += 2; // = Number of long int / posting ***!= ISIS.MANUAL ***
  if (tq->ip > 125) // ***!= ISIS.MANUAL ***
    { // Get next block for this segment; we HAVE TO find the right block!
    tq->ib += 1; tq->ip = 0;
    if (ReadMem (&tq->ibl, bc->ifp, tq->ib - 1, sizeof (ifpBlock)) != 0)
      return (-1);
    if (labs (tq->ibl.ifblk) != tq->ib) return (-3);
    }
  tq->ipst = (char *) &tq->ibl.ifrec [tq->ip];
  return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void PostMake (long int mfn, short int tag, char occ, short int pcnt
              , char *s, char *slen)
  { ifpPosting rnp;
    char *p = (char *) &rnp;

  rnp.pmfn = mfn; rnp.ptag = tag; rnp.pocc = occ; rnp.pcnt = pcnt;
  if (tag == 0) *slen = 3; // Only MFN
  else *slen = 5;          // MFN and TAG
  s [2] = p [0]; s [1] = p [1]; s [0] = p [2]; // = pmfn
  s [4] = p [4]; s [3] = p [5]; s [5] = p [6]; s [7] = p [7]; s [6] = p [8];
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void PostInv (ifpPosting *ifpp, void *ps)
  { char *p = (char *) ifpp;
    char *s = (char *) ps;

  // Posting string:      |0 a |1 b |2 c |3 d |4 e |5 f |6 g |7 h |  ps   *s
  // ifpPosting:     |0  c|1 b |2 a |3 0 |4 e |5 d |6 f |7 h |8 g |  ifpp *p
  //                 |pmfn               |ptag     |pocc|pcnt     |

  p [0] = s [2]; p [1] = s [1]; p [2] = s [0]; p [3] = 0; // = pmfn
  p [4] = s [4]; p [5] = s [3]; p [6] = s [5]; p [7] = s [7]; p [8] = s [6];
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

int main (int argc, char **argv)  // char *argv []
  { char c;

  printf ("ix <database>\n");
  printf ("<database> ::= UNIX-ISIS (v.3) MF without .MST\n");
  printf ("ix ver. 0.3, Copyright (C) 2000 by Robert Janusz\n");
  printf ("ix comes with ABSOLUTELY NO WARRANTY\n");
  printf ("License: GPL; see http://www.gnu.org/ for details\n");

  for (c = 'A'; c <= 'z'; c++) // Let's prepare it, we use API, not an example
    UpCase [c] = UpMe (c);

  if (argv [1] != NULL) {
    strcpy (DBpath, argv [1]);  // Database; no test ! ;-) it's only an example
    isis ();
  }

  return (0);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void isis ()
  { 
    int        j, v, k;
    long int   nrc;
    BaseCtrl   bc;
    RecCtrl    rc;
    char       st [31];
    TableQuery tabq [NFIND];

  strcpy (bc.name, DBpath);
  InitBase (&bc);  

// ShowRec but not all...

  printf ("\n*** ShowRec ***\n\n");

//for (nrc = 1L; nrc < bc.mctrl.nxtmfn; nrc++) {
  for (nrc = 10L; nrc < 20L; nrc++) {
    if ((j = GetRec (&bc, nrc, &rc, RECLENGTH)) == 0) {
      ShowRec (&rc);
    }
  }

// ShowIndex

  printf ("\n*** ShowIndex: %s ***\n\n", st);

  strcpy (st, "TEST"); // just an Inv. File term

  v = 0; // term counter
  k = IndexPrep (&bc, &tabq [0], &tabq [1], st); // prepare indexes
  while (k > 0) {
    j = 20 * k - 10; // We have the term length ;-)
    memcpy (&st, &tabq [k - 1].term [0], j); // keep it in st
    st [j] = 0;
    if ((v += 1) > 12)  // we want 12 terms in a list
      {
      printf ("->%s\n", st);  // the next term
      break;
      }
    printf ("%s\n", st);
    k = IndexTerm (k, &tabq [0], &tabq [1]); // Next term
  }

// Find a term & show records

  printf ("\n*** Search for: %s ***\n\n", st);  // Let's use the last term

  setQuery (&bc, &tabq [0], st, 0);  // String to search in a filed 0 = any
  
  j = Find (&bc, tabq, 1, &nrc, &rc, RECLENGTH); // 1 term to search for

// Clean all

  printf ("\nO.K.\n");

  Done (&bc);
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

void ShowRec (RecCtrl *rc)
  { long int  nr;
    short int n, k, kk;
    char      st [256];
    char      *b   = rc->buf;
    mstHdrTxt *mht = &rc->hdrs.mht;
    mstIndex  *ind = (mstIndex *) &rc->buf [sizeof (RecHeaders)];

  nr = rc->hdrs.mhf.mfn; // Record header

  printf ("=== %li\n", nr);
  for (n = 0; n < mht->nvf; n++)
    { // Show fileds without formating
    printf ("*** [%li] ", (long int) ind->tag);
    k = mht->base + ind->pos;
    for (kk = 0; kk < ind->len; kk++)
      printf ("%c", b [k++]);
    printf ("\n");
    ind += 1; // Next index
    }
  printf ("___\n");
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

// This procedure is prepared to do a simple AND operator after some
// extensions, so it can be localy a little strange ;-)

int Postings (BaseCtrl *bc, TableQuery tabq [], int ifind, long int *npg
             , RecCtrl *rc, long int recvol)
  { int        j, state;
    long  int  ipg, rn;
    ifpPosting ip;
    TableQuery *tq;

  if (ifind < 1) goto finish; // nothing to do

  ipg = 1L; // Index for page break

    { tq = &tabq [0]; // Let's think only about one term to handle (example)
      if (PostSeg (bc, tq) != 0) return (-1);
      tq->iall = tq->ihd.ifptotp; // Only 1st Segment has tot. post. number
      tq->iitot = 0;
      if (PostNext (bc, tq) != 0) return (-1);
      PostInv (&ip, tq->ipst);
      if (sComp (tq->st, tq->term, 20 * tq->it - 10) != 0)
        ipg = 0L; // the term not found
    }

  if (ipg != 1L) goto finish;

  for ( ; ; ) // Global loop...
    {

    // Is there a field
      { tq = &tabq [0];
      if (tq->v != 0)
        while (ip.ptag != tq->v)
          {
          if (PostNext (bc, tq) > 0) goto finish; // No more postings
          PostInv (&ip, tq->ipst);
          }
      }

    rn = ip.pmfn; 
    
    state = 1;
    if (ip.pmfn != rn)
      state = 0;

    if (state) // We have a record to show
      {
      if ((j = GetRec (bc, rn, rc, recvol)) == 0)
        ShowRec (rc);
      else 
        printf ("Record state = %li\n", j); 

        do // Shift all postings
          { tq = &tabq [0];
          if (PostNext (bc, tq) > 0) goto finish; // No more postings
          PostInv (&ip, tq->ipst);
          }
        while (ip.pmfn <= rn); // MFN equals
      }
    else // state == 0, Shift all postings
        while (ip.pmfn < rn)  // The last is to examin
          { tq = &tabq [0];
          if (PostNext (bc, tq) > 0) goto finish; // No more postings
          PostInv (&ip, tq->ipst);
          }
    }

finish:
  return (0); // The output is finished
  }

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++