Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  tabdos.cpp   Sprache: C

 

/************* TabDos C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABDOS                                                */
/* -------------                                                       */
/*  Version 4.9.5                                                      */
/*                                                                     */
/* COPYRIGHT:                                                          */
/* ----------                                                          */
/*  (C) Copyright to the author Olivier BERTRAND          1998-2020    */
/*                                                                     */
/* WHAT THIS PROGRAM DOES:                                             */
/* -----------------------                                             */
/*  This program are the DOS tables classes.                           */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/*  Include relevant sections of the System header files.              */
/***********************************************************************/
#include "my_global.h"
#if defined(_WIN32)
#include <io.h>
#include <sys\timeb.h>                   // For testing only
#include <fcntl.h>
#include <errno.h>
#if defined(__BORLANDC__)
#define __MFC_COMPAT__                   // To define min/max as macro
#endif   // __BORLANDC__
//#include <windows.h>
#else   // !_WIN32
#if defined(UNIX)
#include <errno.h>
#include <unistd.h>
#else   // !UNIX
#include <io.h>
#endif  // !UNIX
#include <fcntl.h>
#endif  // !_WIN32

/***********************************************************************/
/*  Include application header files:                                  */
/*  global.h    is header containing all global declarations.          */
/*  plgdbsem.h  is header containing the DB application declarations.  */
/*  filamtxt.h  is header containing the file AM classes declarations. */
/***********************************************************************/
#include "global.h"
#include "osutil.h"
#include "plgdbsem.h"
//#include "catalog.h"
#include "mycat.h"
#include "xindex.h"
#include "filamap.h"
#include "filamfix.h"
#include "filamdbf.h"
#if defined(GZ_SUPPORT)
#include "filamgz.h"
#endif   // GZ_SUPPORT
#if defined(ZIP_SUPPORT)
#include "filamzip.h"
#endif   // ZIP_SUPPORT
#include "tabdos.h"
#include "tabfix.h"
#include "tabmul.h"
#include "array.h"
#include "blkfil.h"
#include "m_string.h"

/***********************************************************************/
/*  DB static variables.                                               */
/***********************************************************************/
int num_read, num_there, num_eq[2];                 // Statistics

/***********************************************************************/
/*  Size of optimize file header.                                      */
/***********************************************************************/
#define NZ         4

/***********************************************************************/
/*  External function.                                                 */
/***********************************************************************/
bool    ExactInfo(void);
USETEMP UseTemp(void);

/***********************************************************************/
/*  Min and Max blocks contains zero ended fields (blank = false).     */
/*  No conversion of block values (check = true).                      */
/***********************************************************************/
PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len= 0, int prec= 0,
                    bool check= true, bool blank= false, bool un= false);

/* --------------------------- Class DOSDEF -------------------------- */

/***********************************************************************/
/*  Constructor.                                                       */
/***********************************************************************/
DOSDEF::DOSDEF(void)
  {
  Pseudo = 3;
  Fn = NULL;
  Ofn = NULL;
 Entry = NULL;
  To_Indx = NULL;
 Pwd = NULL;
  Recfm = RECFM_VAR;
  Mapped = false;
 Zipped = false;
 Mulentries = false;
 Append = false;
 Padded = false;
  Huge = false;
  Accept = false;
  Eof = false;
  To_Pos = NULL;
  Optimized = 0;
  AllocBlks = 0;
  Compressed = 0;
  Lrecl = 0;
  AvgLen = 0;
  Block = 0;
  Last = 0;
  Blksize = 0;
  Maxerr = 0;
  ReadMode = 0;
  Ending = 0;
  Teds = 0;
  } // end of DOSDEF constructor

/***********************************************************************/
/*  DefineAM: define specific AM block values from XDB file.           */
/***********************************************************************/
bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int)
  {
  char   buf[8];
  bool   map = (am && (*am == 'M' || *am == 'm'));
  LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F"
             : (am && (*am == 'B' || *am == 'b')) ? "B"
           : (am && (*am == 'X' || *am == 'x')) ? "X"
           : (am && !stricmp(am, "DBF"))        ? "D" : "V";

 if ((Zipped = GetBoolCatInfo("Zipped", false))) {
  Entry = GetStringCatInfo(g, "Entry", NULL);
  Mulentries = (Entry && *Entry) ? strchr(Entry, '*') || strchr(Entry, '?')
                                 : false;
  Mulentries = GetBoolCatInfo("Mulentries", Mulentries);
  Append = GetBoolCatInfo("Append", false);
  Pwd = GetStringCatInfo(g, "Password", NULL);
 } // endif Zipped

  Desc = Fn = GetStringCatInfo(g, "Filename", NULL);
  Ofn = GetStringCatInfo(g, "Optname", Fn);
  GetCharCatInfo("Recfm", (PSZ)dfm, buf, sizeof(buf));
  Recfm = (toupper(*buf) == 'F') ? RECFM_FIX :
          (toupper(*buf) == 'B') ? RECFM_BIN :
        (toupper(*buf) == 'X') ? RECFM_NAF : // MGO
        (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR;
  Lrecl = GetIntCatInfo("Lrecl", 0);

  if (Recfm != RECFM_DBF)
    Compressed = GetIntCatInfo("Compressed", 0);

  Mapped = GetBoolCatInfo("Mapped", map);
//Block = GetIntCatInfo("Blocks", 0);
//Last = GetIntCatInfo("Last", 0);
  Ending = GetIntCatInfo("Ending", CRLF);

 if (Ending <= 0) {
  Ending = (Recfm == RECFM_BIN || Recfm == RECFM_VCT) ? 0 : CRLF;
  SetIntCatInfo("Ending", Ending);
 } // endif ending

 if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) {
    Huge = GetBoolCatInfo("Huge", Cat->GetDefHuge());
    Padded = GetBoolCatInfo("Padded", false);
    Blksize = GetIntCatInfo("Blksize", 0);
    Eof = (GetIntCatInfo("EOF", 0) != 0);
    Teds = toupper(*GetStringCatInfo(g, "Endian", ""));
  } else if (Recfm == RECFM_DBF) {
    Maxerr = GetIntCatInfo("Maxerr", 0);
    Accept = GetBoolCatInfo("Accept", false);
    ReadMode = GetIntCatInfo("Readmode", 0);
  } else // (Recfm == RECFM_VAR)
    AvgLen = GetIntCatInfo("Avglen", 0);

  // Ignore wrong Index definitions for catalog commands
  SetIndexInfo();
  return false;
  } // end of DefineAM

/***********************************************************************/
/*  Get the full path/name of the optimization file.                   */
/***********************************************************************/
bool DOSDEF::GetOptFileName(PGLOBAL g, char *filename)
  {
  PCSZ ftype;

  switch (Recfm) {
    case RECFM_VAR: ftype = ".dop"; break;
    case RECFM_FIX: ftype = ".fop"; break;
    case RECFM_BIN: ftype = ".bop"; break;
    case RECFM_VCT: ftype = ".vop"; break;
  case RECFM_CSV: ftype = ".cop"; break;
  case RECFM_DBF: ftype = ".dbp"; break;
    default:
      snprintf(g->Message, sizeof(g->Message), MSG(INVALID_FTYPE), Recfm);
      return true;
    } // endswitch Ftype

  PlugSetPath(filename, Ofn, GetPath());
  strcat(PlugRemoveType(filename, filename), ftype);
  return false;
  } // end of GetOptFileName

/***********************************************************************/
/*  After an optimize error occurred, remove all set optimize values.  */
/***********************************************************************/
void DOSDEF::RemoveOptValues(PGLOBAL g)
  {
  char    filename[_MAX_PATH];
  PCOLDEF cdp;

  // Delete settings of optimized columns
  for (cdp = To_Cols; cdp; cdp = cdp->GetNext())
    if (cdp->GetOpt()) {
      cdp->SetMin(NULL);
      cdp->SetMax(NULL);
      cdp->SetNdv(0);
      cdp->SetNbm(0);
      cdp->SetDval(NULL);
      cdp->SetBmap(NULL);
      } // endif Opt

  // Delete block position setting for not fixed tables
  To_Pos = NULL;
  AllocBlks = 0;

  // Delete any eventually ill formed non matching optimization file
  if (!GetOptFileName(g, filename))
#if defined(_WIN32)
    DeleteFile(filename);
#else    // UNIX
    remove(filename);
#endif   // _WIN32

  Optimized = 0;
  } // end of RemoveOptValues

/***********************************************************************/
/*  DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */
/***********************************************************************/
bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
  {
  PCSZ ftype;
  char filename[_MAX_PATH];
  bool sep, rc = false;

  if (!To_Indx)
    return false;           // No index

  // If true indexes are in separate files
  sep = GetBoolCatInfo("SepIndex", false);

  if (!sep && pxdf) {
    safe_strcpy(g->Message, sizeof(g->Message), MSG(NO_RECOV_SPACE));
    return true;
    } // endif sep

  switch (Recfm) {
    case RECFM_VAR: ftype = ".dnx"; break;
    case RECFM_FIX: ftype = ".fnx"; break;
    case RECFM_BIN: ftype = ".bnx"; break;
    case RECFM_VCT: ftype = ".vnx"; break;
  case RECFM_CSV: ftype = ".cnx"; break;
  case RECFM_DBF: ftype = ".dbx"; break;
    default:
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_RECFM_VAL), Recfm);
      return true;
    } // endswitch Ftype

  /*********************************************************************/
  /*  Check for existence of an index file.                            */
  /*********************************************************************/
  if (sep) {
    // Indexes are save in separate files
#if defined(_WIN32)
    char drive[_MAX_DRIVE];
#else
    char *drive = NULL;
#endif
    char direc[_MAX_DIR];
    char fname[_MAX_FNAME];
    bool all = !pxdf;
    
    if (all)
      pxdf = To_Indx;

    for (; pxdf; pxdf = pxdf->GetNext()) {
      _splitpath(Ofn, drive, direc, fname, NULL);
      safe_strcat(fname, sizeof(fname), "_");
      safe_strcat(fname, sizeof(fname), pxdf->GetName());
      _makepath(filename, drive, direc, fname, ftype);
      PlugSetPath(filename, filename, GetPath());
#if defined(_WIN32)
      if (!DeleteFile(filename))
        rc |= (GetLastError() != ERROR_FILE_NOT_FOUND);
#else    // UNIX
      if (remove(filename))
        rc |= (errno != ENOENT);
#endif   // UNIX

      if (!all)
        break;

      } // endfor pxdf

  } else {  // !sep
    // Drop all indexes, delete the common file
    PlugSetPath(filename, Ofn, GetPath());
    safe_strcat(PlugRemoveType(filename, filename), sizeof(filename), ftype);
#if defined(_WIN32)
    if (!DeleteFile(filename))
      rc = (GetLastError() != ERROR_FILE_NOT_FOUND);
#else    // UNIX
    if (remove(filename))
      rc = (errno != ENOENT);
#endif   // UNIX
  } // endif sep

  if (rc)
    snprintf(g->Message, sizeof(g->Message), MSG(DEL_FILE_ERR), filename);

  return rc;                        // Return true if error
  } // end of DeleteIndexFile

/***********************************************************************/
/*  InvalidateIndex: mark all indexes as invalid.                      */
/***********************************************************************/
bool DOSDEF::InvalidateIndex(PGLOBAL)
  {
  if (To_Indx)
    for (PIXDEF xp = To_Indx; xp; xp = xp->Next)
      xp->Invalid = true;

  return false;
  } // end of InvalidateIndex

/***********************************************************************/
/*  GetTable: makes a new Table Description Block.                     */
/***********************************************************************/
PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
  {
  // Mapping not used for insert
  USETEMP tmp = UseTemp();
  bool    map = Mapped && mode != MODE_INSERT &&
                !(tmp != TMP_NO && Recfm == RECFM_VAR
                                && mode == MODE_UPDATE) &&
                !(tmp == TMP_FORCE &&
                (mode == MODE_UPDATE || mode == MODE_DELETE));
  PTXF    txfp = NULL;
  PTDBASE tdbp;

  /*********************************************************************/
  /*  Allocate table and file processing class of the proper type.     */
  /*  Column blocks will be allocated only when needed.                */
  /*********************************************************************/
 if (Recfm == RECFM_DBF) {
  if (Catfunc == FNC_NO) {
   if (Zipped) {
    if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
     txfp = new(g) UZDFAM(this);
    } else {
     safe_strcpy(g->Message, sizeof(g->Message), "Zipped DBF tables are read only");
     return NULL;
    } // endif's mode

   } else if (map)
    txfp = new(g) DBMFAM(this);
   else
    txfp = new(g) DBFFAM(this);

   tdbp = new(g) TDBFIX(this, txfp);
  } else
   tdbp = new(g) TDBDCL(this);    // Catfunc should be 'C'

 } else if (Zipped) {
#if defined(ZIP_SUPPORT)
  if (Recfm == RECFM_VAR) {
   if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
    txfp = new(g) UNZFAM(this);
   } else if (mode == MODE_INSERT) {
    txfp = new(g) ZIPFAM(this);
   } else {
    safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
    return NULL;
   } // endif's mode

   tdbp = new(g) TDBDOS(this, txfp);
  } else {
   if (mode == MODE_READ || mode == MODE_ANY || mode == MODE_ALTER) {
    txfp = new(g) UZXFAM(this);
   } else if (mode == MODE_INSERT) {
    txfp = new(g) ZPXFAM(this);
   } else {
    safe_strcpy(g->Message, sizeof(g->Message), "UPDATE/DELETE not supported for ZIP");
    return NULL;
   } // endif's mode

   tdbp = new(g)TDBFIX(this, txfp);
  } // endif Recfm

#else   // !ZIP_SUPPORT
  snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "ZIP");
  return NULL;
#endif  // !ZIP_SUPPORT
  } else if (Recfm != RECFM_VAR && Compressed < 2) {
    if (Huge)
      txfp = new(g) BGXFAM(this);
    else if (map)
      txfp = new(g) MPXFAM(this);
    else if (Compressed) {
#if defined(GZ_SUPPORT)
      txfp = new(g) GZXFAM(this);
#else   // !GZ_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ");
      return NULL;
#endif  // !GZ_SUPPORT
    } else
      txfp = new(g) FIXFAM(this);

    tdbp = new(g) TDBFIX(this, txfp);
  } else {
    if (Compressed) {
#if defined(GZ_SUPPORT)
      if (Compressed == 1)
        txfp = new(g) GZFAM(this);
      else
        txfp = new(g) ZLBFAM(this);

#else   // !GZ_SUPPORT
      snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ");
      return NULL;
#endif  // !GZ_SUPPORT
    } else if (map)
      txfp = new(g) MAPFAM(this);
    else
      txfp = new(g) DOSFAM(this);

    // Txfp must be set even for not multiple tables because
    // it is needed when calling Cardinality in GetBlockValues.
    tdbp = new(g) TDBDOS(this, txfp);
  } // endif Recfm

  if (Multiple)
    tdbp = new(g) TDBMUL(tdbp);
  else
    /*******************************************************************/
    /*  For block tables, get eventually saved optimization values.    */
    /*******************************************************************/
    if (tdbp->GetBlockValues(g)) {
      PushWarning(g, tdbp);
//    return NULL;            // causes a crash when deleting index
    } else if (Recfm == RECFM_VAR || Compressed > 1) {
      if (IsOptimized()) {
        if      (map) {
          txfp = new(g) MBKFAM(this);
        } else if (Compressed) {
#if defined(GZ_SUPPORT)
          if (Compressed == 1)
            txfp = new(g) ZBKFAM(this);
          else {
            txfp->SetBlkPos(To_Pos);
            ((PZLBFAM)txfp)->SetOptimized(To_Pos != NULL);
            } // endelse
#else
          snprintf(g->Message, sizeof(g->Message), MSG(NO_FEAT_SUPPORT), "GZ");
          return NULL;
#endif
        } else
          txfp = new(g) BLKFAM(this);

        ((PTDBDOS)tdbp)->SetTxfp(txfp);
        } // endif Optimized

      } // endif Recfm

  return tdbp;
  } // end of GetTable

/* ------------------------ Class TDBDOS ----------------------------- */

/***********************************************************************/
/*  Implementation of the TDBDOS class. This is the common class that  */
/*  contain all that is common between the TDBDOS and TDBMAP classes.  */
/***********************************************************************/
TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
  {
  if ((Txfp = txfp))
    Txfp->SetTdbp(this);

  Lrecl = tdp->Lrecl;
  AvgLen = tdp->AvgLen;
  Ftype = tdp->Recfm;
  To_Line = NULL;
//To_BlkIdx = NULL;
  To_BlkFil = NULL;
  SavFil = NULL;
//Xeval = 0;
  Beval = 0;
  Abort = false;
  Indxd = false;
  } // end of TDBDOS standard constructor

TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
  {
  Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp;
  Lrecl = tdbp->Lrecl;
  AvgLen = tdbp->AvgLen;
  Ftype = tdbp->Ftype;
  To_Line = tdbp->To_Line;
//To_BlkIdx = tdbp->To_BlkIdx;
  To_BlkFil = tdbp->To_BlkFil;
  SavFil = tdbp->SavFil;
//Xeval = tdbp->Xeval;
  Beval = tdbp->Beval;
  Abort = tdbp->Abort;
  Indxd = tdbp->Indxd;
  } // end of TDBDOS copy constructor

// Method
PTDB TDBDOS::Clone(PTABS t)
  {
  PTDB    tp;
  PDOSCOL cp1, cp2;
  PGLOBAL g = t->G;

  tp = new(g) TDBDOS(g, this);

  for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
    cp2 = new(g) DOSCOL(cp1, tp);  // Make a copy
    NewPointer(t, cp1, cp2);
    } // endfor cp1

  return tp;
  } // end of Clone

/***********************************************************************/
/*  Allocate DOS column description block.                             */
/***********************************************************************/
PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
  {
  return new(g) DOSCOL(g, cdp, this, cprec, n);
  } // end of MakeCol

/***********************************************************************/
/*  Print debug information.                                           */
/***********************************************************************/
void TDBDOS::PrintAM(FILE *f, char *m)
  {
  fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);

  if (Txfp->To_File)
    fprintf(f, "%s  File: %s\n", m, Txfp->To_File);

  } // end of PrintAM

/***********************************************************************/
/*  Remake the indexes after the table was modified.                   */
/***********************************************************************/
int TDBDOS::ResetTableOpt(PGLOBAL g, bool dop, bool dox)
  {
  int  prc = RC_OK, rc = RC_OK;

  if (!GetFileLength(g)) {
    // Void table, delete all opt and index files
    PDOSDEF defp = (PDOSDEF)To_Def;

    defp->RemoveOptValues(g);
    return (defp->DeleteIndexFile(g, NULL)) ? RC_INFO : RC_OK;
    } // endif GetFileLength

  MaxSize = -1;                        // Size must be recalculated
  Cardinal = -1;                       // as well as Cardinality

  To_Filter = NULL;                     // Disable filtering
//To_BlkIdx = NULL;                     // and index filtering
  To_BlkFil = NULL;                     // and block filtering

  // After the table was modified the indexes
  // are invalid and we should mark them as such...
  (void)((PDOSDEF)To_Def)->InvalidateIndex(g);

  if (dop) {
    Columns = NULL;                     // Not used anymore

    if (Txfp->Blocked) {
      // MakeBlockValues must be executed in non blocked mode
      // except for ZLIB access method.
      if        (Txfp->GetAmType() == TYPE_AM_MAP) {
        Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
#if defined(GZ_SUPPORT)
      } else if (Txfp->GetAmType() == TYPE_AM_GZ) {
        Txfp = new(g) GZFAM((PDOSDEF)To_Def);
      } else if (Txfp->GetAmType() == TYPE_AM_ZLIB) {
        Txfp->Reset();
        ((PZLBFAM)Txfp)->SetOptimized(false);
#endif   // GZ_SUPPORT
   } else if (Txfp->GetAmType() == TYPE_AM_BLK)
        Txfp = new(g) DOSFAM((PDOSDEF)To_Def);

      Txfp->SetTdbp(this);
    } else
      Txfp->Reset();

    Use = USE_READY;                    // So the table can be reopened
    Mode = MODE_ANY;                    // Just to be clean
    rc = MakeBlockValues(g);            // Redo optimization
    } // endif dop

  if (dox && (rc == RC_OK || rc == RC_INFO)) {
    // Remake eventual indexes
//  if (Mode != MODE_UPDATE)
      To_SetCols = NULL;                // Positions are changed

    Columns = NULL;                     // Not used anymore
    Txfp->Reset();                      // New start
    Use = USE_READY;                    // So the table can be reopened
    Mode = MODE_READ;                   // New mode
    prc = rc;

    if (PlgGetUser(g)->Check & CHK_OPT)
      // We must remake all indexes.
      rc = MakeIndex(g, NULL, false);

    rc = (rc == RC_INFO) ? prc : rc;
    } // endif dox

  return rc;
  } // end of ResetTableOpt

/***********************************************************************/
/*  Calculate the block sizes so block I/O can be used and also the    */
/*  Min/Max values for clustered/sorted table columns.                 */
/***********************************************************************/
int TDBDOS::MakeBlockValues(PGLOBAL g)
  {
  int        i, lg, nrec, rc, n = 0;
  int        curnum, curblk, block, savndv, savnbm;
  void      *savmin, *savmax;
  bool       blocked, xdb2 = false;
//POOLHEADER save;
  PCOLDEF    cdp;
  PDOSDEF    defp = (PDOSDEF)To_Def;
  PDOSCOL    colp = NULL;
  PDBUSER    dup = PlgGetUser(g);
//void      *memp = cat->GetDescp();
  (void) defp->GetCat();                        // XXX Should be removed?


  if ((nrec = defp->GetElemt()) < 2) {
    if (!To_Def->Partitioned()) {
      // This may be wrong to do in some cases
      safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT));
      return RC_INFO;                   // Not to be optimized
    } else
      return RC_OK;

  } else if (GetMaxSize(g) == 0 || !(dup->Check & CHK_OPT)) {
    // Suppress the opt file firstly if the table is void,
    // secondly when it was modified with OPTIMIZATION unchecked
    // because it is no more valid.
    defp->RemoveOptValues(g);           // Erase opt file
    return RC_OK;                       // void table
  } else if (MaxSize < 0)
    return RC_FX;

  defp->SetOptimized(0);

  // Estimate the number of needed blocks
 if ((block = (int)((MaxSize + (int)nrec - 1) / (int)nrec)) < 2) {
  // This may be wrong to do in some cases
  defp->RemoveOptValues(g);
  safe_strcpy(g->Message, sizeof(g->Message), MSG(TABLE_NOT_OPT));
  return RC_INFO;                   // Not to be optimized
 } // endif block

  // We have to use local variables because Txfp->CurBlk is set
  // to Rows+1 by unblocked variable length table access methods.
  curblk = -1;
  curnum = nrec - 1;
//last = 0;
  Txfp->Block = block;                  // This is useful mainly for
  Txfp->CurBlk = curblk;                // blocked tables (ZLBFAM), for
  Txfp->CurNum = curnum;                // others it is just to be clean.

  /*********************************************************************/
  /*  Allocate the array of block starting positions.                  */
  /*********************************************************************/
//if (memp)
//  save = *(PPOOLHEADER)memp;

  Txfp->BlkPos = (int*)PlugSubAlloc(g, NULL, (block + 1) * sizeof(int));

  /*********************************************************************/
  /*  Allocate the blocks for clustered columns.                       */
  /*********************************************************************/
  blocked = Txfp->Blocked;         // Save
  Txfp->Blocked = true;            // So column block can be allocated

  for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
    if (cdp->GetOpt()) {
      lg = cdp->GetClen();

      if (cdp->GetFreq() && cdp->GetFreq() <= dup->Maxbmp) {
        cdp->SetXdb2(true);
        savndv = cdp->GetNdv();
        cdp->SetNdv(0);              // Reset Dval number of values
        xdb2 = true;
        savmax = cdp->GetDval();
        cdp->SetDval(PlugSubAlloc(g, NULL, cdp->GetFreq() * lg));
        savnbm = cdp->GetNbm();
        cdp->SetNbm(0);              // Prevent Bmap allocation
//      savmin = cdp->GetBmap();
//      cdp->SetBmap(PlugSubAlloc(g, NULL, block * sizeof(int)));

        if (trace(1))
          htrc("Dval(%p) Bmap(%p) col(%d) %s Block=%d lg=%d\n",
              cdp->GetDval(), cdp->GetBmap(), i, cdp->GetName(), block, lg);

        // colp will be initialized with proper Dval VALBLK
        colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
        colp->InitValue(g);          // Allocate column value buffer
        cdp->SetNbm(savnbm);
//      cdp->SetBmap(savmin);        // Can be reused if the new size
        cdp->SetDval(savmax);        // is not greater than this one.
        cdp->SetNdv(savndv);
      } else {
        cdp->SetXdb2(false);         // Maxbmp may have been reset
        savmin = cdp->GetMin();
        savmax = cdp->GetMax();
        cdp->SetMin(PlugSubAlloc(g, NULL, block * lg));
        cdp->SetMax(PlugSubAlloc(g, NULL, block * lg));

        // Valgrind complains if there are uninitialised bytes
        // after the null character ending
        if (IsTypeChar(cdp->GetType())) {
          memset(cdp->GetMin(), 0, block * lg);
          memset(cdp->GetMax(), 0, block * lg);
          } // endif Type

        if (trace(1))
          htrc("min(%p) max(%p) col(%d) %s Block=%d lg=%d\n",
              cdp->GetMin(), cdp->GetMax(), i, cdp->GetName(), block, lg);

        // colp will be initialized with proper opt VALBLK's
        colp = (PDOSCOL)MakeCol(g, cdp, colp, i);
        colp->InitValue(g);          // Allocate column value buffer
        cdp->SetMin(savmin);         // Can be reused if the number
        cdp->SetMax(savmax);         // of blocks does not change.
      } // endif Freq

      } // endif Clustered

  // No optimised columns. Still useful for blocked variable tables.
  if (!colp && defp->Recfm != RECFM_VAR) {
    safe_strcpy(g->Message, sizeof(g->Message), "No optimised columns");
    return RC_INFO;
    } // endif colp

  Txfp->Blocked = blocked;

  /*********************************************************************/
  /*  Now do calculate the optimization values.                        */
  /*********************************************************************/
  Mode = MODE_READ;

  if (OpenDB(g))
    return RC_FX;

  if (xdb2) {
    /*********************************************************************/
    /*  Retrieve the distinct values of XDB2 columns.                    */
    /*********************************************************************/
    if (GetDistinctColumnValues(g, nrec))
      return RC_FX;

    OpenDB(g);                   // Rewind the table file
    } // endif xdb2

#if defined(PROG_INFO)
  /*********************************************************************/
  /*  Initialize progress information                                  */
  /*********************************************************************/
  char   *p = (char *)PlugSubAlloc(g, NULL, 24 + strlen(Name));

  snprintf(p,  24 + strlen(Name), "%s%s", MSG(OPTIMIZING), Name);
  dup->Step = p;
  dup->ProgMax = GetProgMax(g);
  dup->ProgCur = 0;
#endif   // SOCKET_MODE  ||         THREAD

  /*********************************************************************/
  /*  Make block starting pos and min/max values of cluster columns.   */
  /*********************************************************************/
  while ((rc = ReadDB(g)) == RC_OK) {
    if (blocked) {
      // A blocked FAM class handles CurNum and CurBlk (ZLBFAM)
      if (!Txfp->CurNum)
        Txfp->BlkPos[Txfp->CurBlk] = Txfp->GetPos();

    } else {
      if (++curnum >= nrec) {
        if (++curblk >= block) {
          safe_strcpy(g->Message, sizeof(g->Message), MSG(BAD_BLK_ESTIM));
          goto err;
        } else
          curnum = 0;

        // Get block starting position
        Txfp->BlkPos[curblk] = Txfp->GetPos();
        } // endif CurNum

//    last = curnum + 1;              // curnum is zero based
      Txfp->CurBlk = curblk;          // Used in COLDOS::SetMinMax
      Txfp->CurNum = curnum;          // Used in COLDOS::SetMinMax
    } // endif blocked

    /*******************************************************************/
    /*  Now calculate the min and max values for the cluster columns.  */
    /*******************************************************************/
    for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->GetNext())
      if (colp->Clustered == 2) {
        if (colp->SetBitMap(g))
          goto err;

      } else
        if (colp->SetMinMax(g))
          goto err;                   // Currently: column is not sorted

#if defined(PROG_INFO)
    if (!dup->Step) {
      safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
      goto err;
    } else
      dup->ProgCur = GetProgCur();
#endif   // PROG_INFO

    n++;           // Used to calculate block and last
    } // endwhile

  if (rc == RC_EF) {
    Txfp->Nrec = nrec;

#if 0 // No good because Curblk and CurNum after EOF are different
      // depending on whether the file is mapped or not mapped.
    if (blocked) {
//    Txfp->Block = Txfp->CurBlk + 1;
      Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum : nrec;
//    Txfp->Last = (Txfp->CurNum) ? Txfp->CurNum + 1 : nrec;
      Txfp->Block = Txfp->CurBlk + (Txfp->Last == nrec ? 0 : 1);
    } else {
      Txfp->Block = curblk + 1;
      Txfp->Last = last;
    } // endif blocked
#endif // 0

    // New values of Block and Last
    Txfp->Block = (n + nrec - 1) / nrec;
    Txfp->Last = (n % nrec) ? (n % nrec) : nrec; 

    // This is needed to be able to calculate the last block size
    Txfp->BlkPos[Txfp->Block] = Txfp->GetNextPos();
  } else
    goto err;

  /*********************************************************************/
  /*  Save the optimization values for this table.                     */
  /*********************************************************************/
  if (!SaveBlockValues(g)) {
    defp->Block = Txfp->Block;
    defp->Last = Txfp->Last;
    CloseDB(g);
    defp->SetIntCatInfo("Blocks", Txfp->Block);
    defp->SetIntCatInfo("Last", Txfp->Last);
    return RC_OK;
    } // endif SaveBlockValues

 err:
  // Restore Desc memory suballocation
//if (memp)
//  *(PPOOLHEADER)memp = save;

  defp->RemoveOptValues(g);
  CloseDB(g);
  return RC_FX;
  } // end of MakeBlockValues

/***********************************************************************/
/*  Save the block and Min/Max values for this table.                  */
/*  The problem here is to avoid name duplication, because more than   */
/*  one data file can have the same name (but different types) and/or  */
/*  the same data file can be used with different block sizes. This is */
/*  why we use Ofn that defaults to the file name but can be set to a  */
/*  different name if necessary.                                       */
/***********************************************************************/
bool TDBDOS::SaveBlockValues(PGLOBAL g)
  {
  char    filename[_MAX_PATH];
  int     lg, n[NZ + 2];
  size_t  nbk, ndv, nbm, block = Txfp->Block;
  bool    rc = false;
  FILE   *opfile;
  PDOSCOL colp;
  PDOSDEF defp = (PDOSDEF)To_Def;

  if (defp->GetOptFileName(g, filename))
    return true;

  if (!(opfile = fopen(filename, "wb"))) {
    snprintf(g->Message, sizeof(g->Message), MSG(OPEN_MODE_ERROR),
            "wb", (int)errno, filename);
    safe_strcat(g->Message, sizeof(g->Message), ": ");
    safe_strcat(g->Message, sizeof(g->Message), strerror(errno));

    if (trace(1))
      htrc("%s\n", g->Message);

    return true;
    } // endif opfile

  memset(n, 0, sizeof(n));     // To avoid valgrind warning

  if (Ftype == RECFM_VAR || defp->Compressed == 2) {
    /*******************************************************************/
    /*  Write block starting positions into the opt file.              */
    /*******************************************************************/
    block++;
    lg = sizeof(int);
    n[0] = Txfp->Last; n[1] = lg; n[2] = Txfp->Nrec; n[3] = Txfp->Block;

    if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
      snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_WR_ERR), strerror(errno));
      rc = true;
      } // endif size

    if (fwrite(Txfp->BlkPos, lg, block, opfile) != block) {
      snprintf(g->Message, sizeof(g->Message), MSG(OPTBLK_WR_ERR), strerror(errno));
      rc = true;
      } // endif size

    block--;                       // = Txfp->Block;
    } // endif Ftype

  /*********************************************************************/
  /*  Write the Min/Max values into the opt file.                      */
  /*********************************************************************/
  for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next) {
    lg = colp->Value->GetClen();

    //  Now start the writing process
    if (colp->Clustered == 2) {
      // New XDB2 block optimization. Will be recognized when reading
      // because the column index is negated.
      ndv = colp->Ndv; nbm = colp->Nbm;
      nbk = nbm * block;
      n[0] = -colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;
      n[4] = ndv; n[5] = nbm;

      if (fwrite(n, sizeof(int), NZ + 2, opfile) != NZ + 2) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

      if (fwrite(colp->Dval->GetValPointer(), lg, ndv, opfile) != ndv) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_DVAL_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

      if (fwrite(colp->Bmap->GetValPointer(), sizeof(int), nbk, opfile) != nbk) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_BMAP_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

    } else {
      n[0] = colp->Index; n[1] = lg; n[2] = Txfp->Nrec; n[3] = block;

      if (fwrite(n, sizeof(int), NZ, opfile) != NZ) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

      if (fwrite(colp->Min->GetValPointer(), lg, block, opfile) != block) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_MIN_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

      if (fwrite(colp->Max->GetValPointer(), lg, block, opfile) != block) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_MAX_WR_ERR), strerror(errno));
        rc = true;
        } // endif size

    } // endif Clustered

    } // endfor colp

  fclose(opfile);
  return rc;
  } // end of SaveBlockValues

/***********************************************************************/
/*  Read the Min/Max values for this table.                            */
/*  The problem here is to avoid name duplication, because more than   */
/*  one data file can have the same name (but different types) and/or  */
/*  the same data file can be used with different block sizes. This is */
/*  why we use Ofn that defaults to the file name but can be set to a  */
/*  different name if necessary.                                       */
/***********************************************************************/
bool TDBDOS::GetBlockValues(PGLOBAL g)
  {
  char       filename[_MAX_PATH];
  int        i, lg, n[NZ];
  int        nrec, block = 0, last = 0;
  int        len;
  bool       newblk = false;
  size_t     ndv, nbm, nbk, blk;
  FILE      *opfile;
  PCOLDEF    cdp;
  PDOSDEF    defp = (PDOSDEF)To_Def;
  PDBUSER    dup = PlgGetUser(g);

  (void) defp->GetCat();                        // XXX Should be removed?


#if 0
  if (Mode == MODE_INSERT && Txfp->GetAmType() == TYPE_AM_DOS)
    return false;
#endif   // _WIN32

 if (defp->Optimized || !(dup->Check & CHK_OPT))
    return false;                   // Already done or to be redone

  if (Ftype == RECFM_VAR || defp->Compressed == 2) {
    /*******************************************************************/
    /*  Variable length file that can be read by block.                */
    /*******************************************************************/
    nrec = (defp->GetElemt()) ? defp->GetElemt() : 1;
      
    if (nrec > 1) {
      // The table can be declared optimized if it is void.
      // This is useful to handle Insert in optimized mode.
      char filename[_MAX_PATH];
      int  h;
      int  flen = -1;

      PlugSetPath(filename, defp->Fn, GetPath());
      h = open(filename, O_RDONLY);
      flen = (h == -1 && errno == ENOENT) ? 0 : _filelength(h);

      if (h != -1)
        close(h);

      if (!flen) {
        defp->SetOptimized(1);
        return false;
       } // endif flen

    } else
      return false;                 // Not optimisable

    cdp = defp->GetCols();
    i = 1;
  } else {
    /*******************************************************************/
    /*  Fixed length file. Opt file exists only for clustered columns. */
    /*******************************************************************/
    // Check for existence of clustered columns
    for (cdp = defp->GetCols(), i = 1; cdp; cdp = cdp->GetNext(), i++)
      if (cdp->GetOpt())
        break;

    if (!cdp)
      return false;            // No optimization needed

    if ((len = Cardinality(g)) < 0)
      return true;             // Table error
    else if (!len)
      return false;            // File does not exist yet

    block = Txfp->Block;       // Was set in Cardinality
    nrec = Txfp->Nrec;
  } // endif Ftype

  if (defp->GetOptFileName(g, filename))
    return true;

  if (!(opfile = fopen(filename, "rb")))
    return false;                   // No saved values

  if (Ftype == RECFM_VAR || defp->Compressed == 2) {
    /*******************************************************************/
    /*  Read block starting positions from the opt file.               */
    /*******************************************************************/
    lg = sizeof(int);

    if (fread(n, sizeof(int), NZ, opfile) != NZ) {
      snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_RD_ERR), strerror(errno));
      goto err;
      } // endif size

    if (n[1] != lg || n[2] != nrec) {
      snprintf(g->Message, sizeof(g->Message), MSG(OPT_NOT_MATCH), filename);
      goto err;
      } // endif

    last = n[0];
    block = n[3];
    blk = block + 1;

    defp->To_Pos = (int*)PlugSubAlloc(g, NULL, blk * lg);

    if (fread(defp->To_Pos, lg, blk, opfile) != blk) {
      snprintf(g->Message, sizeof(g->Message), MSG(OPTBLK_RD_ERR), strerror(errno));
      goto err;
      } // endif size

    } // endif Ftype

  /*********************************************************************/
  /*  Read the Min/Max values from the opt file.                       */
  /*********************************************************************/
  for (; cdp; cdp = cdp->GetNext(), i++)
    if (cdp->GetOpt()) {
      lg = cdp->GetClen();
      blk = block;

      //  Now start the reading process.
      if (fread(n, sizeof(int), NZ, opfile) != NZ) {
        snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_RD_ERR), strerror(errno));
        goto err;
        } // endif size

      if (n[0] == -i) {
        // Read the XDB2 opt values from the opt file
        if (n[1] != lg || n[2] != nrec || n[3] != block) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_NOT_MATCH), filename);
          goto err;
          } // endif

        if (fread(n, sizeof(int), 2, opfile) != 2) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_HEAD_RD_ERR), strerror(errno));
          goto err;
          } // endif fread

        ndv = n[0]; nbm = n[1]; nbk = nbm * blk;

        if (cdp->GetNdv() < (int)ndv || !cdp->GetDval())
          cdp->SetDval(PlugSubAlloc(g, NULL, ndv * lg));

        cdp->SetNdv((int)ndv);

        if (fread(cdp->GetDval(), lg, ndv, opfile) != ndv) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_DVAL_RD_ERR), strerror(errno));
          goto err;
          } // endif size

        if (newblk || cdp->GetNbm() < (int)nbm || !cdp->GetBmap())
          cdp->SetBmap(PlugSubAlloc(g, NULL, nbk * sizeof(int)));

        cdp->SetNbm((int)nbm);

        if (fread(cdp->GetBmap(), sizeof(int), nbk, opfile) != nbk) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_BMAP_RD_ERR), strerror(errno));
          goto err;
          } // endif size

        cdp->SetXdb2(true);
      } else {
        // Read the Min/Max values from the opt file
        if (n[0] != i || n[1] != lg || n[2] != nrec || n[3] != block) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_NOT_MATCH), filename);
          goto err;
          } // endif

        if (newblk || !cdp->GetMin())
          cdp->SetMin(PlugSubAlloc(g, NULL, blk * lg));

        if (fread(cdp->GetMin(), lg, blk, opfile) != blk) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_MIN_RD_ERR), strerror(errno));
          goto err;
          } // endif size

        if (newblk || !cdp->GetMax())
          cdp->SetMax(PlugSubAlloc(g, NULL, blk * lg));

        if (fread(cdp->GetMax(), lg, blk, opfile) != blk) {
          snprintf(g->Message, sizeof(g->Message), MSG(OPT_MAX_RD_ERR), strerror(errno));
          goto err;
          } // endif size

        cdp->SetXdb2(false);
      } // endif n[0] (XDB2)

      } // endif Clustered

  defp->SetBlock(block);
  defp->Last = last;     // For Cardinality
  defp->SetAllocBlks(block);
  defp->SetOptimized(1);
  fclose(opfile);
  MaxSize = -1;          // Can be refined later
  return false;

 err:
  defp->RemoveOptValues(g);
  fclose(opfile);

  // Ignore error if not in mode CHK_OPT
  return (PlgGetUser(g)->Check & CHK_OPT) != 0;
  } // end of GetBlockValues

/***********************************************************************/
/*  This fonction is used while making XDB2 block optimization.        */
/*  It constructs for each elligible columns, the sorted list of the   */
/*  distinct values existing in the column. This function uses an      */
/*  algorithm that permit to get several sets of distinct values by    */
/*  reading the table only once, which cannot be done using a standard */
/*  SQL query.                                                         */
/***********************************************************************/
bool TDBDOS::GetDistinctColumnValues(PGLOBAL g, int nrec)
  {
  char   *p;
  int     rc, blk, n = 0;
  PDOSCOL colp;
  PDBUSER dup = PlgGetUser(g);

  /*********************************************************************/
  /*  Initialize progress information                                  */
  /*********************************************************************/
  p = (char *)PlugSubAlloc(g, NULL, 48 + strlen(Name));
  snprintf(p, 48 + strlen(Name), "%s%s", MSG(GET_DIST_VALS), Name);
  dup->Step = p;
  dup->ProgMax = GetProgMax(g);
  dup->ProgCur = 0;

  while ((rc = ReadDB(g)) == RC_OK) {
    for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
      if (colp->Clustered == 2)
        if (colp->AddDistinctValue(g))
          return true;                   // Too many distinct values

#if defined(SOCKET_MODE)
    if (SendProgress(dup)) {
      safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
      return true;
    } else
#elif defined(THREAD)
    if (!dup->Step) {
      safe_strcpy(g->Message, sizeof(g->Message), MSG(OPT_CANCELLED));
      return true;
    } else
#endif     // THREAD
      dup->ProgCur = GetProgCur();

    n++;
    } // endwhile

  if (rc != RC_EF)
    return true;

  // Reset the number of table blocks
//nrec = ((PDOSDEF)To_Def)->GetElemt(); (or default value)
  blk = (n + nrec - 1) / nrec;
  Txfp->Block = blk;                    // Useful mainly for ZLBFAM ???

  // Set Nbm, Bmap for XDB2 columns
  for (colp = (PDOSCOL)Columns; colp; colp = (PDOSCOL)colp->Next)
    if (colp->Clustered == 2) {
//    colp->Cdp->SetNdv(colp->Ndv);
      colp->Nbm = (colp->Ndv + MAXBMP - 1) / MAXBMP;
      colp->Bmap = AllocValBlock(g, NULL, TYPE_INT, colp->Nbm * blk);
      } // endif Clustered

  return false;
  } // end of GetDistinctColumnValues

/***********************************************************************/
/*  Analyze the filter and construct the Block Evaluation Filter.      */
/*  This is possible when a filter contains predicates implying a      */
/*  column marked as "clustered" or "sorted" matched to a constant     */
/*  argument. It is then possible by comparison against the smallest   */
/*  and largest column values in each block to determine whether the   */
/*  filter condition will be always true or always false for the block.*/
/***********************************************************************/
PBF TDBDOS::InitBlockFilter(PGLOBAL g, PFIL filp)
  {
  bool blk = Txfp->Blocked;

  if (To_BlkFil)
    return To_BlkFil;      // Already done
  else if (!filp)
    return NULL;
  else if (blk) {
    if (Txfp->GetAmType() == TYPE_AM_DBF)
      /*****************************************************************/
      /*  If RowID is used in this query, block optimization cannot be */
      /*  used because currently the file must be read sequentially.   */
      /*****************************************************************/
      for (PCOL cp = Columns; cp; cp = cp->GetNext())
        if (cp->GetAmType() == TYPE_AM_ROWID && !((RIDBLK*)cp)->GetRnm())
          return NULL;

    } // endif blk

  int  i, op = filp->GetOpc(), opm = filp->GetOpm();
  bool cnv[2];
  PCOL colp;
  PXOB arg[2] = {NULL,NULL};
  PBF *fp = NULL, bfp = NULL;

  switch (op) {
    case OP_EQ:
    case OP_NE:
    case OP_GT:
    case OP_GE:
    case OP_LT:
    case OP_LE:
      if (! opm) {
        for (i = 0; i < 2; i++) {
          arg[i] = filp->Arg(i);
          cnv[i] = filp->Conv(i);
          } // endfor i

        bfp = CheckBlockFilari(g, arg, op, cnv);
        break;
        } // endif !opm

      // if opm, pass thru
      // fall through
    case OP_IN:
      if (filp->GetArgType(0) == TYPE_COLBLK &&
          filp->GetArgType(1) == TYPE_ARRAY) {
        arg[0] = filp->Arg(0);
        arg[1] = filp->Arg(1);
        colp = (PCOL)arg[0];

        if (colp->GetTo_Tdb() == this) {
          // Block evaluation is possible for...
          if (colp->GetAmType() == TYPE_AM_ROWID) {
            // Special column ROWID and constant array, but
            // currently we don't know how to retrieve a RowID
            // from a DBF table that is not sequentially read.
//          if (Txfp->GetAmType() != TYPE_AM_DBF ||
//              ((RIDBLK*)arg[0])->GetRnm())
              bfp = new(g) BLKSPCIN(g, this, op, opm, arg, Txfp->Nrec);

          } else if (blk && Txfp->Nrec > 1 && colp->IsClustered())
          {
            // Clustered column and constant array
            if (colp->GetClustered() == 2)
              bfp = new(g) BLKFILIN2(g, this, op, opm, arg);
            else
              bfp = new(g) BLKFILIN(g, this, op, opm, arg);
          }
        } // endif this

#if 0
      } else if (filp->GetArgType(0) == TYPE_SCALF &&
                 filp->GetArgType(1) == TYPE_ARRAY) {
        arg[0] = filp->Arg(0);
        arg[1] = filp->Arg(1);

        if (((PSCALF)arg[0])->GetOp() == OP_ROW &&
            arg[1]->GetResultType() == TYPE_LIST) {
          PARRAY  par = (PARRAY)arg[1];
          LSTVAL *vlp = (LSTVAL*)par->GetValue();

          ((SFROW*)arg[0])->GetParms(n);

          if (n != vlp->GetN())
            return NULL;
          else
            n = par->GetNval();

          arg[1] = new(g) CONSTANT(vlp);
          fp = (PBF*)PlugSubAlloc(g, NULL, n * sizeof(PBF));
          cnv[0] = cnv[1] = false;

          if (op == OP_IN)
            op = OP_EQ;

          for (i = 0; i < n; i++) {
            par->GetNthValue(vlp, i);

            if (!(fp[i] = CheckBlockFilari(g, arg, op, cnv)))
              return NULL;

            } // endfor i

          bfp = new(g) BLKFILLOG(this, (opm == 2 ? OP_AND : OP_OR), fp, n);
          } // endif ROW
#endif // 0

      } // endif Type

      break;
    case OP_AND:
    case OP_OR:
      fp = (PBF*)PlugSubAlloc(g, NULL, 2 * sizeof(PBF));
      fp[0] = InitBlockFilter(g, (PFIL)(filp->Arg(0)));
      fp[1] = InitBlockFilter(g, (PFIL)(filp->Arg(1)));

      if (fp[0] || fp[1])
        bfp = new(g) BLKFILLOG(this, op, fp, 2);

      break;
    case OP_NOT:
      fp = (PBF*)PlugSubAlloc(g, NULL, sizeof(PBF));

      if ((*fp = InitBlockFilter(g, (PFIL)(filp->Arg(0)))))
        bfp = new(g) BLKFILLOG(this, op, fp, 1);

      break;
    case OP_LIKE:
    default:
      break;
    } // endswitch op

  return bfp;
  } // end of InitBlockFilter

/***********************************************************************/
/*  Analyze the passed arguments and construct the Block Filter.       */
/***********************************************************************/
PBF TDBDOS::CheckBlockFilari(PGLOBAL g, PXOB *arg, int op, bool *cnv)
  {
//int     i, n1, n2, ctype = TYPE_ERROR, n = 0, type[2] = {0,0};
//bool    conv = false, xdb2 = false, ok = false, b[2];
//PXOB   *xarg1, *xarg2 = NULL, xp[2];
  int     i, n = 0, type[2] = {0,0};
  bool    conv = false, xdb2 = false;
  PXOB    xp[2];
  PCOL    colp;
  PBF     bfp = NULL;

  for (i = 0; i < 2; i++) {
    switch (arg[i]->GetType()) {
      case TYPE_CONST:
        type[i] = 1;
 //     ctype = arg[i]->GetResultType();
        break;
      case TYPE_COLBLK:
        conv = cnv[i];
        colp = (PCOL)arg[i];

        if (colp->GetTo_Tdb() == this) {
          if (colp->GetAmType() == TYPE_AM_ROWID) {
            // Currently we don't know how to retrieve a RowID
            // from a DBF table that is not sequentially read.
//          if (Txfp->GetAmType() != TYPE_AM_DBF ||
//              ((RIDBLK*)arg[i])->GetRnm())
              type[i] = 5;

          } else if (Txfp->Blocked && Txfp->Nrec > 1 &&
                     colp->IsClustered()) {
            type[i] = 2;
            xdb2 = colp->GetClustered() == 2;
            } // endif Clustered

        } else if (colp->GetColUse(U_CORREL)) {
          // This is a column pointing to the outer query of a
          // correlated subquery, it has a constant value during
          // each execution of the subquery.
          type[i] = 1;
//        ctype = arg[i]->GetResultType();
        } // endif this

        break;
//    case TYPE_SCALF:
//      if (((PSCALF)arg[i])->GetOp() == OP_ROW) {
//        sfr[i] = (SFROW*)arg[i];
//        type[i] = 7;
//        } // endif Op

//      break;
      default:
        break;
      } // endswitch ArgType

    if (!type[i])
      break;

    n += type[i];
    } // endfor i

  if (n == 3 || n == 6) {
    if (conv) {
      // The constant has not the good type and will not match
      // the block min/max values. Warn and abort.
      snprintf(g->Message, sizeof(g->Message), "Block opt: %s", MSG(VALTYPE_NOMATCH));
      PushWarning(g, this);
      return NULL;
      } // endif Conv

    if (type[0] == 1) {
      // Make it always as Column-op-Value
      *xp = arg[0];
      arg[0] = arg[1];
      arg[1] = *xp;

      switch (op) {
        case OP_GT: op = OP_LT; break;
        case OP_GE: op = OP_LE; break;
        case OP_LT: op = OP_GT; break;
        case OP_LE: op = OP_GE; break;
        } // endswitch op

      } // endif

#if defined(_DEBUG)
//  assert(arg[0]->GetResultType() == ctype);
#endif

    if (n == 3) {
      if (xdb2) {
        if (((PDOSCOL)arg[0])->GetNbm() == 1)
          bfp = new(g) BLKFILAR2(g, this, op, arg);
        else    // Multiple bitmap made of several ULONG's
          bfp = new(g) BLKFILMR2(g, this, op, arg);
      } else
        bfp = new(g) BLKFILARI(g, this, op, arg);

    } else // n = 6
      bfp = new(g) BLKSPCARI(this, op, arg, Txfp->Nrec);

#if 0
  } else if (n == 8 || n == 14) {
    if (n == 8 && ctype != TYPE_LIST) {
      // Should never happen
      safe_strcpy(g->Message, sizeof(g->Message), "Block opt: bad constant");
   throw 99;
  } // endif Conv

    if (type[0] == 1) {
      // Make it always as Column-op-Value
      sfr[0] = sfr[1];
      arg[1] = arg[0];

      switch (op) {
        case OP_GT: op = OP_LT; break;
        case OP_GE: op = OP_LE; break;
        case OP_LT: op = OP_GT; break;
        case OP_LE: op = OP_GE; break;
        } // endswitch op

      } // endif

    xarg1 = sfr[0]->GetParms(n1);

    if (n == 8) {
      vlp = (LSTVAL*)arg[1]->GetValue();
      n2 = vlp->GetN();
      xp[1] = new(g) CONSTANT((PVAL)NULL);
    } else
      xarg2 = sfr[1]->GetParms(n2);

    if (n1 != n2)
      return NULL;             // Should we flag an error ?

    fp = (PBF*)PlugSubAlloc(g, NULL, n1 * sizeof(PBF));

    for (i = 0; i < n1; i++) {
      xp[0] = xarg1[i];

      if (n == 8)
        ((CONSTANT*)xp[1])->SetValue(vlp->GetSubVal(i));
      else
        xp[1] = xarg2[i];

      b[0] = b[1] = (xp[0]->GetResultType() != xp[1]->GetResultType());
      ok |= ((fp[i] = CheckBlockFilari(g, xp, op, b)) != NULL);
      } // endfor i

    if (ok)
      bfp = new(g) BLKFILLOG(this, OP_AND, fp, n1);
#endif // 0

  } // endif n

  return bfp;
  } // end of CheckBlockFilari

/***********************************************************************/
/*  ResetBlkFil: reset the block filter and restore filtering, or make */
/*  the block filter if To_Filter was not set when opening the table.  */
/***********************************************************************/
void TDBDOS::ResetBlockFilter(PGLOBAL g)
  {
  if (!To_BlkFil) {
    if (To_Filter)
      if ((To_BlkFil = InitBlockFilter(g, To_Filter))) {
        htrc("BlkFil=%p\n", To_BlkFil);
        MaxSize = -1;      // To be recalculated
        } // endif To_BlkFil
    
    return;
    } // endif To_BlkFil

  To_BlkFil->Reset(g);

  if (SavFil && !To_Filter) {
    // Restore filter if it was disabled by optimization
    To_Filter = SavFil;
    SavFil = NULL;
    } // endif

  Beval = 0;
  } // end of ResetBlockFilter

/***********************************************************************/
/*  Block optimization: evaluate the block index filter against        */
/*  the min and max values of this block and return:                   */
/*  RC_OK: if some records in the block can meet filter criteria.      */
/*  RC_NF: if no record in the block can meet filter criteria.         */
/*  RC_EF: if no record in the remaining file can meet filter criteria.*/
/*  In addition, temporarily suppress filtering if all the records in  */
/*  the block meet filter criteria.                                    */
/***********************************************************************/
int TDBDOS::TestBlock(PGLOBAL g)
  {
  int rc = RC_OK;

  if (To_BlkFil && Beval != 2) {
    // Check for block filtering evaluation
    if (Beval == 1) {
      // Filter was removed for last block, restore it
      To_Filter = SavFil;
      SavFil = NULL;
      } // endif Beval

    // Check for valid records in new block
    switch (Beval = To_BlkFil->BlockEval(g)) {
      case -2:            // No more valid values in file
        rc = RC_EF;
        break;
      case -1:            // No valid values in block
        rc = RC_NF;
        break;
      case 1:             // All block values are valid
      case 2:             // All subsequent file values are Ok
        // Before suppressing the filter for the block(s) it is
        // necessary to reset the filtered columns to NOT_READ
        // so their new values are retrieved by the SELECT list.
        if (To_Filter) // Can be NULL when externally called (XDB)
          To_Filter->Reset();

        SavFil = To_Filter;
        To_Filter = NULL; // So remove filter
      } // endswitch Beval

    if (trace(1))
      htrc("BF Eval Beval=%d\n", Beval);

    } // endif To_BlkFil

  return rc;
  } // end of TestBlock

/***********************************************************************/
/*  Check whether we have to create/update permanent indexes.          */
/***********************************************************************/
int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
  {
 int     k, n, rc = RC_OK;
 bool    fixed, doit, sep;
  PCOL   *keycols, colp;
  PIXDEF  xdp, sxp = NULL;
  PKPDEF  kdp;
  PDOSDEF dfp;
//PCOLDEF cdp;
  PXINDEX x;
  PXLOAD  pxp;

  Mode = MODE_READ;
  Use = USE_READY;
  dfp = (PDOSDEF)To_Def;

  if (!Cardinality(g)) {
    // Void table erase eventual index file(s)
    (void)dfp->DeleteIndexFile(g, NULL);
    return RC_OK;
  } else
    fixed = Ftype != RECFM_VAR;

  // Are we are called from CreateTable or CreateIndex?
  if (pxdf) {
    if (!add && dfp->GetIndx()) {
      safe_strcpy(g->Message, sizeof(g->Message), MSG(INDX_EXIST_YET));
      return RC_FX;
      } // endif To_Indx

    if (add && dfp->GetIndx()) {
      for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext())
        if (!stricmp(sxp->GetName(), pxdf->GetName())) {
          snprintf(g->Message, sizeof(g->Message), MSG(INDEX_YET_ON), pxdf->GetName(), Name);
          return RC_FX;
        } else if (!sxp->GetNext())
          break;

      sxp->SetNext(pxdf);
//    first = false;
    } else
      dfp->SetIndx(pxdf);

//  pxdf->SetDef(dfp);
  } else if (!(pxdf = dfp->GetIndx()))
    return RC_INFO;              // No index to make

 try {
  // Allocate all columns that will be used by indexes.
 // This must be done before opening the table so specific
 // column initialization can be done (in particular by TDBVCT)
  for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext())
   for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
    if (!(colp = ColDB(g, kdp->GetName(), 0))) {
     snprintf(g->Message, sizeof(g->Message), MSG(INDX_COL_NOTIN), kdp->GetName(), Name);
     goto err;
    } else if (colp->GetResultType() == TYPE_DECIM) {
     snprintf(g->Message, sizeof(g->Message), "Decimal columns are not indexable yet");
     goto err;
    } // endif Type

    colp->InitValue(g);
    n = MY_MAX(n, xdp->GetNparts());
   } // endfor kdp

  keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL));
  sep = dfp->GetBoolCatInfo("SepIndex", false);

  /*********************************************************************/
  /*  Construct and save the defined indexes.                          */
  /*********************************************************************/
  for (xdp = pxdf; xdp; xdp = xdp->GetNext())
   if (!OpenDB(g)) {
    if (xdp->IsAuto() && fixed)
     // Auto increment key and fixed file: use an XXROW index
     continue;      // XXROW index doesn't need to be made

    // On Update, redo only indexes that are modified
    doit = !To_SetCols;
    n = 0;

    if (sxp)
     xdp->SetID(sxp->GetID() + 1);

    for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
     // Check whether this column was updated
     for (colp = To_SetCols; !doit && colp; colp = colp->GetNext())
      if (!stricmp(kdp->GetName(), colp->GetName()))
       doit = true;

     keycols[n++] = ColDB(g, kdp->GetName(), 0);
    } // endfor kdp

   // If no indexed columns were updated, don't remake the index
   // if indexes are in separate files.
    if (!doit && sep)
     continue;

    k = xdp->GetNparts();

    // Make the index and save it
    if (dfp->Huge)
     pxp = new(g) XHUGE;
    else
     pxp = new(g) XFILE;

    if (k == 1)            // Simple index
     x = new(g) XINDXS(this, xdp, pxp, keycols);
    else                   // Multi-Column index
     x = new(g) XINDEX(this, xdp, pxp, keycols);

    if (!x->Make(g, sxp)) {
     // Retreive define values from the index
     xdp->SetMaxSame(x->GetMaxSame());
     //      xdp->SetSize(x->GetSize());

         // store KXYCOL Mxs in KPARTDEF Mxsame
     xdp->SetMxsame(x);

#if defined(TRACE)
     printf("Make done...\n");
#endif   // TRACE

     //      if (x->GetSize() > 0)
     sxp = xdp;

     xdp->SetInvalid(false);
    } else
     goto err;

   } else
    return RC_INFO;     // Error or Physical table does not exist

 } catch (int n) {
  if (trace(1))
   htrc("Exception %d: %s\n", n, g->Message);
  rc = RC_FX;
 } catch (const char *msg) {
  safe_strcpy(g->Message, sizeof(g->Message), msg);
  rc = RC_FX;
 } // end catch

 if (Use == USE_OPEN)
    CloseDB(g);

  return rc;

err:
  if (sxp)
    sxp->SetNext(NULL);
  else
    dfp->SetIndx(NULL);

  return RC_FX;
  } // end of MakeIndex

/***********************************************************************/
/*  Make a dynamic index.                                              */
/***********************************************************************/
bool TDBDOS::InitialyzeIndex(PGLOBAL g, volatile PIXDEF xdp, bool sorted)
{
  int     k;
  volatile bool dynamic;
  bool    brc;
  PCOL    colp;
  PCOLDEF cdp;
  PVAL    valp;
  PXLOAD  pxp;
  volatile PKXBASE kxp;
  PKPDEF  kdp;

  if (!xdp && !(xdp = To_Xdp)) {
    safe_strcpy(g->Message, sizeof(g->Message), "NULL dynamic index");
    return true;
  } else
    dynamic = To_Filter && xdp->IsUnique() && xdp->IsDynamic();
//  dynamic = To_Filter && xdp->IsDynamic();      NIY

  // Allocate the key columns definition block
  Knum = xdp->GetNparts();
  To_Key_Col = (PCOL*)PlugSubAlloc(g, NULL, Knum * sizeof(PCOL));

  // Get the key column description list
  for (k = 0, kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext())
    if (!(colp = ColDB(g, kdp->GetName(), 0)) || colp->InitValue(g)) {
      snprintf(g->Message, sizeof(g->Message), "Wrong column %s", kdp->GetName());
      return true;
    } else
      To_Key_Col[k++] = colp;

#if defined(_DEBUG)
  if (k != Knum) {
    snprintf(g->Message, sizeof(g->Message), "Key part number mismatch for %s",
                        xdp->GetName());
    return 0;
    } // endif k
#endif   // _DEBUG

  // Allocate the pseudo constants that will contain the key values
  To_Link = (PXOB*)PlugSubAlloc(g, NULL, Knum * sizeof(PXOB));

  for (k = 0, kdp = xdp->GetToKeyParts(); kdp; k++, kdp = kdp->GetNext()) {
    if ((cdp = Key(k)->GetCdp()))
      valp = AllocateValue(g, cdp->GetType(), cdp->GetLength());
    else {                        // Special column ?
      colp = Key(k);
      valp = AllocateValue(g, colp->GetResultType(), colp->GetLength());
    } // endif cdp

    To_Link[k]= new(g) CONSTANT(valp);
    } // endfor k

  // Make the index on xdp
  if (!xdp->IsAuto()) {
    if (!dynamic) {
      if (((PDOSDEF)To_Def)->Huge)
        pxp = new(g) XHUGE;
      else
        pxp = new(g) XFILE;

    } else
      pxp = NULL;

    if (Knum == 1)            // Single index
      kxp = new(g) XINDXS(this, xdp, pxp, To_Key_Col, To_Link);
    else                      // Multi-Column index
      kxp = new(g) XINDEX(this, xdp, pxp, To_Key_Col, To_Link);

  } else                      // Column contains same values as ROWID
    kxp = new(g) XXROW(this);

 try {
    if (dynamic) {
      ResetBlockFilter(g);
      kxp->SetDynamic(dynamic);
      brc = kxp->Make(g, xdp);
    } else
      brc = kxp->Init(g);

    if (!brc) {
      if (Txfp->GetAmType() == TYPE_AM_BLK) {
        // Cannot use indexing in DOS block mode
        Txfp = new(g) DOSFAM((PBLKFAM)Txfp, (PDOSDEF)To_Def);
        Txfp->AllocateBuffer(g);
        To_BlkFil = NULL;
        } // endif AmType

      To_Kindex= kxp;

      if (!(sorted && To_Kindex->IsSorted()) &&
          ((Mode == MODE_UPDATE && IsUsingTemp(g)) ||
           (Mode == MODE_DELETE && Txfp->GetAmType() != TYPE_AM_DBF)))
        Indxd = true;

      } // endif brc

 } catch (int n) {
  if (trace(1))
   htrc("Exception %d: %s\n", n, g->Message);
  brc = true;
 } catch (const char *msg) {
  safe_strcpy(g->Message, sizeof(g->Message), msg);
  brc = true;
 } // end catch

 return brc;
} // end of InitialyzeIndex

/***********************************************************************/
/*  DOS GetProgMax: get the max value for progress information.        */
/***********************************************************************/
int TDBDOS::GetProgMax(PGLOBAL g)
  {
  return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g);
  } // end of GetProgMax

/***********************************************************************/
/*  DOS GetProgCur: get the current value for progress information.    */
/***********************************************************************/
int TDBDOS::GetProgCur(void)
  {
  return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos();
  } // end of GetProgCur

/***********************************************************************/
/*  RowNumber: return the ordinal number of the current row.           */
/***********************************************************************/
int TDBDOS::RowNumber(PGLOBAL g, bool)
  {
  if (To_Kindex) {
    /*******************************************************************/
    /*  Don't know how to retrieve RowID from file address.            */
    /*******************************************************************/
    snprintf(g->Message, sizeof(g->Message), MSG(NO_ROWID_FOR_AM),
                        GetAmName(g, Txfp->GetAmType()));
    return 0;
  } else
    return Txfp->GetRowID();

  } // end of RowNumber

/***********************************************************************/
/*  DOS Cardinality: returns table cardinality in number of rows.      */
/*  This function can be called with a null argument to test the       */
/*  availability of Cardinality implementation (1 yes, 0 no).          */
/***********************************************************************/
int TDBDOS::Cardinality(PGLOBAL g)
  {
  int n = Txfp->Cardinality(NULL);

  if (!g)
    return (Mode == MODE_ANY) ? 1 : n;

  if (Cardinal < 0) {
    if (!Txfp->Blocked && n == 0) {
      // Info command, we try to return exact row number
      PDOSDEF dfp = (PDOSDEF)To_Def;
      PIXDEF  xdp = dfp->To_Indx;

      if (xdp && xdp->IsValid()) {
        // Cardinality can be retreived from one index
        PXLOAD  pxp;
    
        if (dfp->Huge)
          pxp = new(g) XHUGE;
        else
          pxp = new(g) XFILE;
    
        PXINDEX kxp = new(g) XINDEX(this, xdp, pxp, NULL, NULL);
    
        if (!(kxp->GetAllSizes(g, Cardinal)))
          return Cardinal;
    
        } // endif Mode

      if (Mode == MODE_ANY && ExactInfo()) {
        // Using index impossible or failed, do it the hard way
        Mode = MODE_READ;
        To_Line = (char*)PlugSubAlloc(g, NULL, (size_t)Lrecl + 1);
    
        if (Txfp->OpenTableFile(g))
          return (Cardinal = Txfp->Cardinality(g));
    
        for (Cardinal = 0; n != RC_EF;)
          if (!(n = Txfp->ReadBuffer(g)))
            Cardinal++;
    
        Txfp->CloseTableFile(g, false);
        Mode = MODE_ANY;
      } else {
        // Return the best estimate
        int len = GetFileLength(g);

        if (len >= 0) {
          int rec;

          if (trace(1))
            htrc("Estimating lines len=%d ending=%d/n",
                  len, ((PDOSDEF)To_Def)->Ending);

          /*************************************************************/
          /* Estimate the number of lines in the table (if not known)  */
          /* by dividing the file length by the average record length. */
          /*************************************************************/
          rec = ((PDOSDEF)To_Def)->Ending;

          if (AvgLen <= 0)          // No given average estimate
            rec += EstimatedLength();
          else   // An estimate was given for the average record length
            rec += AvgLen;

          Cardinal = (len + rec - 1) / rec;

          if (trace(1))
            htrc("avglen=%d MaxSize%d\n", rec, Cardinal);

          } // endif len

      } // endif Mode

    } else
      Cardinal = Txfp->Cardinality(g);

    } // endif Cardinal

  return Cardinal;
  } // end of Cardinality

/***********************************************************************/
/*  DOS GetMaxSize: returns file size estimate in number of lines.     */
/*  This function covers variable record length files.                 */
/***********************************************************************/
int TDBDOS::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize >= 0)
    return MaxSize;

  if (!Cardinality(NULL)) {
    int len = GetFileLength(g);

    if (len >= 0) {
      int rec;

      if (trace(1))
        htrc("Estimating lines len=%d ending=%d/n",
              len, ((PDOSDEF)To_Def)->Ending);

      /*****************************************************************/
      /*  Estimate the number of lines in the table (if not known) by  */
      /*  dividing the file length by minimum record length.           */
      /*****************************************************************/
      rec = EstimatedLength() + ((PDOSDEF)To_Def)->Ending;
      MaxSize = (len + rec - 1) / rec;

      if (trace(1))
        htrc("avglen=%d MaxSize%d\n", rec, MaxSize);

      } // endif len

  } else
    MaxSize = Cardinality(g);

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  DOS EstimatedLength. Returns an estimated minimum line length.     */
/***********************************************************************/
int TDBDOS::EstimatedLength(void)
  {
  int     dep = 0;
  PCOLDEF cdp = To_Def->GetCols();

  if (!cdp->GetNext()) {
    // One column table, we are going to return a ridiculous
    // result if we set dep to 1
    dep = 1 + cdp->GetLong() / 20;           // Why 20 ?????
  } else for (; cdp; cdp = cdp->GetNext())
  if (!(cdp->Flags & (U_VIRTUAL|U_SPECIAL)))
      dep = MY_MAX(dep, cdp->GetOffset());

  return (int)dep;
  } // end of Estimated Length

/***********************************************************************/
/*  DOS tables favor the use temporary files for Update.               */
/***********************************************************************/
bool TDBDOS::IsUsingTemp(PGLOBAL)
  {
  USETEMP utp = UseTemp();

  return (utp == TMP_YES || utp == TMP_FORCE ||
         (utp == TMP_AUTO && Mode == MODE_UPDATE));
  } // end of IsUsingTemp

/***********************************************************************/
/*  DOS Access Method opening routine.                                 */
/*  New method now that this routine is called recursively (last table */
/*  first in reverse order): index blocks are immediately linked to    */
/*  join block of next table if it exists or else are discarted.       */
/***********************************************************************/
bool TDBDOS::OpenDB(PGLOBAL g)
  {
  if (trace(1))
    htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
          this, Tdb_No, Use, Mode);

  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open, just replace it at its beginning.          */
    /*******************************************************************/
    if (!To_Kindex) {
      Txfp->Rewind();       // see comment in Work.log

      if (SkipHeader(g))
        return true;

    } else
      /*****************************************************************/
      /*  Table is to be accessed through a sorted index table.        */
      /*****************************************************************/
      To_Kindex->Reset();

    ResetBlockFilter(g);
    return false;
    } // endif use

  if (Mode == MODE_DELETE && !Next && Txfp->GetAmType() != TYPE_AM_DOS
#if defined(BSON_SUPPORT)
                                   && Txfp->GetAmType() != TYPE_AM_BIN
#endif   // BSON_SUPPORT
                                 && Txfp->GetAmType() != TYPE_AM_MGO) {
    // Delete all lines. Not handled in MAP or block mode
    Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
    Txfp->SetTdbp(this);
  } else if (Txfp->Blocked && (Mode == MODE_DELETE ||
             (Mode == MODE_UPDATE && UseTemp() != TMP_NO))) {
    /*******************************************************************/
    /*  Delete is not currently handled in block mode neither Update   */
    /*  when using a temporary file.                                   */
    /*******************************************************************/
    if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE)
      Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
#if defined(GZ_SUPPORT)
    else if (Txfp->GetAmType() == TYPE_AM_GZ)
      Txfp = new(g) GZFAM((PDOSDEF)To_Def);
#endif   // GZ_SUPPORT
    else // if (Txfp->GetAmType() != TYPE_AM_DOS)    ???
      Txfp = new(g) DOSFAM((PDOSDEF)To_Def);

    Txfp->SetTdbp(this);
    } // endif Mode

  /*********************************************************************/
  /*  Open according to logical input/output mode required.            */
  /*  Use conventionnal input/output functions.                        */
  /*  Treat files as binary in Delete mode (for line moving)           */
  /*********************************************************************/
  if (Txfp->OpenTableFile(g))
    return true;

  Use = USE_OPEN;       // Do it now in case we are recursively called

  /*********************************************************************/
  /*  Allocate the block filter tree if evaluation is possible.        */
  /*********************************************************************/
  To_BlkFil = InitBlockFilter(g, To_Filter);

  /*********************************************************************/
 /*  Lrecl does not include line ending  */
 /*********************************************************************/
 size_t linelen = Lrecl + ((PDOSDEF)To_Def)->Ending + 1;

  To_Line = (char*)PlugSubAlloc(g, NULL, linelen);

  if (Mode == MODE_INSERT) {
    // Spaces between fields must be filled with blanks
    memset(To_Line, ' ', Lrecl);
    To_Line[Lrecl] = '\0';
  } else
    memset(To_Line, 0, linelen);

  if (trace(1))
    htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);

  if (SkipHeader(g))         // When called from CSV/FMT files
    return true;

  /*********************************************************************/
  /*  Reset statistics values.                                         */
  /*********************************************************************/
  num_read = num_there = num_eq[0] = num_eq[1] = 0;
  return false;
  } // end of OpenDB

/***********************************************************************/
/*  ReadDB: Data Base read routine for DOS access method.              */
/***********************************************************************/
int TDBDOS::ReadDB(PGLOBAL g)
  {
  if (trace(2))
    htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n",
          GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line);

  if (To_Kindex) {
    /*******************************************************************/
    /*  Reading is by an index table.                                  */
    /*******************************************************************/
    int recpos = To_Kindex->Fetch(g);

    switch (recpos) {
      case -1:           // End of file reached
        return RC_EF;
      case -2:           // No match for join
        return RC_NF;
      case -3:           // Same record as non null last one
        num_there++;
        return RC_OK;
      default:
        /***************************************************************/
        /*  Set the file position according to record to read.         */
        /***************************************************************/
        if (SetRecpos(g, recpos))
          return RC_FX;

        if (trace(2))
          htrc("File position is now %d\n", GetRecpos());

        if (Mode == MODE_READ)
          /*************************************************************/
          /*  Defer physical reading until one column setting needs it */
          /*  as it can be a big saving on joins where no other column */
          /*  than the keys are used, so reading is unnecessary.       */
          /*************************************************************/
          if (Txfp->DeferReading())
            return RC_OK;

      } // endswitch recpos

    } // endif To_Kindex

  if (trace(2))
    htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line);

  /*********************************************************************/
  /*  Now start the reading process.                                   */
  /*********************************************************************/
  return ReadBuffer(g);
  } // end of ReadDB

/***********************************************************************/
/*  PrepareWriting: Prepare the line to write.                         */
/***********************************************************************/
bool TDBDOS::PrepareWriting(PGLOBAL)
  {
  if (Ftype == RECFM_VAR && (Mode == MODE_INSERT || Txfp->GetUseTemp())) {
    char *p;

    /*******************************************************************/
    /*  Suppress trailing blanks.                                      */
    /*  Also suppress eventual null from last line.                    */
    /*******************************************************************/
    for (p = To_Line + Lrecl -1; p >= To_Line; p--)
      if (*p && *p != ' ')
        break;

    *(++p) = '\0';
    } // endif Mode

  return false;
  } // end of PrepareWriting

/***********************************************************************/
/*  WriteDB: Data Base write routine for DOS access method.            */
/***********************************************************************/
int TDBDOS::WriteDB(PGLOBAL g)
  {
  if (trace(2))
    htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);

  // Make the line to write
  if (PrepareWriting(g))
    return RC_FX;

  if (trace(2))
    htrc("Write: line is='%s'\n", To_Line);

  // Now start the writing process
  return Txfp->WriteBuffer(g);
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for DOS (and FIX) access method.     */
/*  RC_FX means delete all. Nothing to do here (was done at open).     */
/***********************************************************************/
int TDBDOS::DeleteDB(PGLOBAL g, int irc)
  {
    return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc);
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for DOS access method.                     */
/***********************************************************************/
void TDBDOS::CloseDB(PGLOBAL g)
  {
  if (To_Kindex) {
    To_Kindex->Close();
    To_Kindex = NULL;
    } // endif

  Txfp->CloseTableFile(g, Abort);
  RestoreNrec();
  } // end of CloseDB

// ------------------------ DOSCOL functions ----------------------------

/***********************************************************************/
/*  DOSCOL public constructor (also called by MAPCOL).                 */
/***********************************************************************/
DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PCSZ am)
      : COLBLK(cdp, tp, i)
  {
  char *p;
  int   prec = Format.Prec;
  PTXF  txfp = ((PTDBDOS)tp)->Txfp;

  assert(cdp);

  if (cp) {
    Next = cp->GetNext();
    cp->SetNext(this);
  } else {
    Next = tp->GetColumns();
    tp->SetColumns(this);
  } // endif cprec

  // Set additional Dos access method information for column.
  Deplac = cdp->GetOffset();
  Long = cdp->GetLong();
  To_Val = NULL;
  Clustered = cdp->GetOpt();
  Sorted = (cdp->GetOpt() == 2) ? 1 : 0;
  Ndv = 0;                // Currently used only for XDB2
  Nbm = 0;                // Currently used only for XDB2
  Min = NULL;
  Max = NULL;
  Bmap = NULL;
  Dval = NULL;
  Buf = NULL;

  if (txfp && txfp->Blocked && Opt && (cdp->GetMin() || cdp->GetDval())) {
    int nblk = txfp->GetBlock();

    Clustered = (cdp->GetXdb2()) ? 2 : 1;
    Sorted = (cdp->GetOpt() > 1) ? 1 : 0;   // Currently ascending only

    if (Clustered == 1) {
      Min = AllocValBlock(g, cdp->GetMin(), Buf_Type, nblk, Long, prec);
      Max = AllocValBlock(g, cdp->GetMax(), Buf_Type, nblk, Long, prec);
    } else {        // Clustered == 2
      // Ndv is the number of distinct values in Dval. Ndv and Nbm
      // may be 0 when optimizing because Ndval is not filled yet,
      // but the size of the passed Dval memory block is Ok.
      Ndv = cdp->GetNdv();
      Dval = AllocValBlock(g, cdp->GetDval(), Buf_Type, Ndv, Long, prec);

      // Bmap cannot be allocated when optimizing, we must know Nbm first
      if ((Nbm = cdp->GetNbm()))
        Bmap = AllocValBlock(g, cdp->GetBmap(), TYPE_INT, Nbm * nblk);

    } // endif Clustered

    } // endif Opt

  OldVal = NULL;                  // Currently used only in MinMax
  Dsp = 0;
  Ldz = false;
  Nod = false;
  Dcm = -1;
  p = cdp->GetFmt();
  Buf = NULL;

  if (p && IsTypeNum(Buf_Type)) {
    // Formatted numeric value
    for (; p && *p && isalpha(*p); p++)
      switch (toupper(*p)) {
        case 'Z':                 // Have leading zeros
          Ldz = true;
          break;
        case 'N':                 // Have no decimal point
          Nod = true;
          break;
        case 'D':                 // Decimal separator
          Dsp = *(++p);
          break;
        } // endswitch p

    // Set number of decimal digits
    Dcm = (*p) ? atoi(p) : GetScale();
    } // endif fmt

  if (trace(1))
    htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);

  } // end of DOSCOL constructor

/***********************************************************************/
/*  DOSCOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
  {
  Deplac = col1->Deplac;
  Long = col1->Long;
  To_Val = col1->To_Val;
  Ldz = col1->Ldz;
  Dsp = col1->Dsp;
  Nod = col1->Nod;
  Dcm = col1->Dcm;
  OldVal = col1->OldVal;
  Buf = col1->Buf;
  Clustered = col1->Clustered;
  Sorted = col1->Sorted;
  Min = col1->Min;
  Max = col1->Max;
  Bmap = col1->Bmap;
  Dval = col1->Dval;
  Ndv = col1->Ndv;
  Nbm = col1->Nbm;
  } // end of DOSCOL copy constructor

/***********************************************************************/
/*  VarSize: This function tells UpdateDB whether or not the block     */
/*  optimization file must be redone if this column is updated, even   */
/*  it is not sorted or clustered. This applies to the last column of  */
/*  a variable length table that is blocked, because if it is updated  */
/*  using a temporary file, the block size may be modified.            */
/***********************************************************************/
bool DOSCOL::VarSize(void)
  {
  PTDBDOS tdbp = (PTDBDOS)To_Tdb;
  PTXF    txfp = tdbp->Txfp;

  if (Cdp && !Cdp->GetNext()               // Must be the last column
          && tdbp->Ftype == RECFM_VAR      // of a DOS variable length
          && txfp->Blocked                 // blocked table
          && txfp->GetUseTemp())           // using a temporary file.
    return true;
  else
    return false;

  } // end VarSize

/***********************************************************************/
/*  SetBuffer: prepare a column block for write operation.             */
/***********************************************************************/
bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
  {
  if (!(To_Val = value)) {
    snprintf(g->Message, sizeof(g->Message), MSG(VALUE_ERROR), Name);
    return true;
  } else if (Buf_Type == value->GetType()) {
    // Values are of the (good) column type
    if (Buf_Type == TYPE_DATE) {
      // If any of the date values is formatted
      // output format must be set for the receiving table
      if (GetDomain() || ((DTVAL *)value)->IsFormatted())
        goto newval;          // This will make a new value;

    } else if (Buf_Type == TYPE_DOUBLE)
      // Float values must be written with the correct (column) precision
      // Note: maybe this should be forced by ShowValue instead of this ?
      value->SetPrec(GetScale());

    Value = value;            // Directly access the external value
  } else {
    // Values are not of the (good) column type
    if (check) {
      snprintf(g->Message, sizeof(g->Message), MSG(TYPE_VALUE_ERR), Name,
              GetTypeName(Buf_Type), GetTypeName(value->GetType()));
      return true;
      } // endif check

 newval:
    if (InitValue(g))         // Allocate the matching value block
      return true;

  } // endif's Value, Buf_Type

  // Allocate the buffer used in WriteColumn for numeric columns
 if (!Buf && IsTypeNum(Buf_Type))
  Buf = (char*)PlugSubAlloc(g, NULL, MY_MAX(64, Long + 1));
 else // Text columns do not need additional buffer
  Buf = (char*)Value->GetTo_Val();

  // Because Colblk's have been made from a copy of the original TDB in
  // case of Update, we must reset them to point to the original one.
  if (To_Tdb->GetOrig())
    To_Tdb = (PTDB)To_Tdb->GetOrig();

  // Set the Column
  Status = (ok) ? BUF_EMPTY : BUF_NO;
  return false;
  } // end of SetBuffer

/***********************************************************************/
/*  ReadColumn: what this routine does is to access the last line      */
/*  read from the corresponding table, extract from it the field       */
/*  corresponding to this column and convert it to buffer type.        */
/***********************************************************************/
void DOSCOL::ReadColumn(PGLOBAL g)
  {
  char   *p = NULL;
  int     i, rc;
  int     field;
  bool    err = false;
  double  dval;
  PTDBDOS tdbp = (PTDBDOS)To_Tdb;

  if (trace(2))
    htrc(
      "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
         Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);

  /*********************************************************************/
  /*  If physical reading of the line was deferred, do it now.         */
  /*********************************************************************/
  if (!tdbp->IsRead())
    if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
      if (rc == RC_EF)
        snprintf(g->Message, sizeof(g->Message), MSG(INV_DEF_READ), rc);

   throw 11;
  } // endif

  p = tdbp->To_Line + Deplac;
  field = Long;

  /*********************************************************************/
  /*  For a variable length file, check if the field exists.           */
  /*********************************************************************/
  if ((tdbp->Ftype == RECFM_VAR || tdbp->Ftype == RECFM_CSV)
    && strlen(tdbp->To_Line) < (unsigned)Deplac)
    field = 0;
  else if (Dsp)
    for(i = 0; i < field; i++)
      if (p[i] == Dsp)
        p[i] = '.';

  switch (tdbp->Ftype) {
    case RECFM_VAR:
    case RECFM_FIX:            // Fixed length text file
  case RECFM_CSV:            // Variable length CSV or FMT file
  case RECFM_DBF:            // Fixed length DBase file
      if (Nod) switch (Buf_Type) {
        case TYPE_INT:
        case TYPE_SHORT:
        case TYPE_TINY:
        case TYPE_BIGINT:
          err = Value->SetValue_char(p, field - Dcm);
          break;
        case TYPE_DOUBLE:
          if (!(err = Value->SetValue_char(p, field))) {
            dval = Value->GetFloatValue();

            for (i = 0; i < Dcm; i++)
              dval /= 10.0;

            Value->SetValue(dval);
          } // endif err

          break;
        default:
          err = Value->SetValue_char(p, field);

          if (!err && Buf_Type == TYPE_DECIM) {
            char* s = Value->GetCharValue();

            if (!(err = ((i = strlen(s)) >= Value->GetClen()))) {
              for (int d = Dcm + 1; d; i--, d--)
                s[i + 1] = s[i];

              s[i + 1] = '.';
            } // endif err

          } // endif DECIM

          break;
      } // endswitch Buf_Type

      else
        err = Value->SetValue_char(p, field);

      break;
    default:
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_RECFM), tdbp->Ftype);
   throw 34;
 } // endswitch Ftype

  if (err) {
    snprintf(g->Message, sizeof(g->Message), "Out of range value for column %s at row %d",
      Name, tdbp->RowNumber(g));
    PushWarning(g, tdbp);
  } // endif err

  // Set null when applicable
  if (Nullable)
    Value->SetNull(Value->IsZero());

  } // end of ReadColumn

/***********************************************************************/
/*  WriteColumn: what this routine does is to access the last line     */
/*  read from the corresponding table, and rewrite the field           */
/*  corresponding to this column from the column buffer and type.      */
/***********************************************************************/
void DOSCOL::WriteColumn(PGLOBAL g)
  {
  char   *p, fmt[32];
  int     i, k, n, len, field;
  PTDBDOS tdbp = (PTDBDOS)To_Tdb;

  if (trace(2))
    htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
          Name, tdbp->GetTdb_No(), ColUse, Status);

  p = tdbp->To_Line + Deplac;

  if (trace(2))
    htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long);

  field = Long;

  if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) {
    len = (signed)strlen(tdbp->To_Line);

    if (tdbp->IsUsingTemp(g))
      // Because of eventual missing field(s) the buffer must be reset
      memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len);
    else
      // The size actually available must be recalculated
      field = MY_MIN(len - Deplac, Long);

    } // endif Ftype

  if (trace(2))
    htrc("Long=%d field=%d coltype=%d colval=%p\n",
          Long, field, Buf_Type, Value);

  /*********************************************************************/
  /*  Get the string representation of Value according to column type. */
  /*********************************************************************/
  if (Value != To_Val)
    Value->SetValue_pval(To_Val, false);    // Convert the updated value

  /*********************************************************************/
  /*  This test is only useful for compressed(2) tables.               */
  /*********************************************************************/
  if (tdbp->Ftype != RECFM_BIN) {
    if (Ldz || Nod || Dcm >= 0) {
      switch (Buf_Type) {
        case TYPE_SHORT:
          safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*hd" : "%*.hd");
          i = 0;

          if (Nod)
            for (; i < Dcm; i++)
              safe_strcat(fmt, sizeof(fmt), "0");

          len = snprintf(Buf, field + 1, fmt, field - i, Value->GetShortValue());
          break;
        case TYPE_INT:
          safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
          i = 0;

          if (Nod)
            for (; i < Dcm; i++)
              safe_strcat(fmt,sizeof(fmt), "0");

          len = snprintf(Buf, field + 1, fmt, field - i, Value->GetIntValue());
          break;
        case TYPE_TINY:
          safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*d" : "%*.d");
          i = 0;

          if (Nod)
            for (; i < Dcm; i++)
              safe_strcat(fmt, sizeof(fmt), "0");

          len = snprintf(Buf, field + 1, fmt, field - i, Value->GetTinyValue());
          break;
        case TYPE_DOUBLE:
        case TYPE_DECIM:
          safe_strcpy(fmt, sizeof(fmt), (Ldz) ? "%0*.*lf" : "%*.*lf");
     len = field + ((Nod && Dcm) ? 1 : 0);
          snprintf(Buf, len + 1, fmt, len, Dcm, Value->GetFloatValue());
          len = strlen(Buf);

          if (Nod && Dcm)
            for (i = k = 0; i < len; i++, k++)
              if (Buf[i] != ' ') {
                if (Buf[i] == '.')
                  k++;

                Buf[i] = Buf[k];
                } // endif Buf(i)

          len = strlen(Buf);
          break;
        default:
          snprintf(g->Message, sizeof(g->Message), "Invalid field format for column %s", Name);
     throw 31;
   } // endswitch BufType

   n = strlen(Buf);
    } else                 // Standard CONNECT format
      n = Value->ShowValue(Buf, field);

    if (trace(1))
      htrc("new length(%p)=%d\n", Buf, n);

    if ((len = n) > field) {
   char *p = Value->GetCharString(Buf);

      snprintf(g->Message, sizeof(g->Message), MSG(VALUE_TOO_LONG), p, Name, field);
   throw 31;
  } else if (Dsp)
      for (i = 0; i < len; i++)
        if (Buf[i] == '.')
          Buf[i] = Dsp; 

    if (trace(2))
      htrc("buffer=%s\n", Buf);

    /*******************************************************************/
    /*  Updating must be done only when not in checking pass.          */
    /*******************************************************************/
    if (Status) {
      memset(p, ' ', field);
      memcpy(p, Buf, len);

      if (trace(2))
        htrc(" col write: '%.*s'\n", len, p);

    } // endif Status

  } else    // BIN compressed table
    /*******************************************************************/
    /*  Check if updating is Ok, meaning col value is not too long.    */
    /*  Updating to be done only during the second pass (Status=true)  */
    /*******************************************************************/
    if (Value->GetBinValue(p, Long, Status)) {
      snprintf(g->Message, sizeof(g->Message), MSG(BIN_F_TOO_LONG),
                          Name, Value->GetSize(), Long);
      throw 31;
    } // endif

  } // end of WriteColumn

/***********************************************************************/
/*  SetMinMax: Calculate minimum and maximum values for one block.     */
/*  Note: TYPE_STRING is stored and processed with zero ended strings  */
/*  to be matching the way the FILTER Eval function processes them.    */
/***********************************************************************/
bool DOSCOL::SetMinMax(PGLOBAL g)
  {
  PTDBDOS tp = (PTDBDOS)To_Tdb;

  ReadColumn(g);           // Extract column value from current line

  if (CheckSorted(g))
    return true;

  if (!tp->Txfp->CurNum) {
    Min->SetValue(Value, tp->Txfp->CurBlk);
    Max->SetValue(Value, tp->Txfp->CurBlk);
  } else {
    Min->SetMin(Value, tp->Txfp->CurBlk);
    Max->SetMax(Value, tp->Txfp->CurBlk);
  } // endif CurNum

  return false;
  } // end of SetMinMax

/***********************************************************************/
/*  SetBitMap: Calculate the bit map of existing values in one block.  */
/*  Note: TYPE_STRING is processed with zero ended strings             */
/*  to be matching the way the FILTER Eval function processes them.    */
/***********************************************************************/
bool DOSCOL::SetBitMap(PGLOBAL g)
  {
  int     i, m, n;
  uint   *bmp;
  PTDBDOS tp = (PTDBDOS)To_Tdb;
  PDBUSER dup = PlgGetUser(g);

  n = tp->Txfp->CurNum;
  bmp = (uint*)Bmap->GetValPtr(Nbm * tp->Txfp->CurBlk);

  // Extract column value from current line
  ReadColumn(g);

  if (CheckSorted(g))
    return true;

  if (!n)                      // New block
    for (m = 0; m < Nbm; m++)
      bmp[m] = 0;             // Reset the new bit map

  if ((i = Dval->Find(Value)) < 0) {
    char buf[32];

    snprintf(g->Message, sizeof(g->Message), MSG(DVAL_NOTIN_LIST),
      Value->GetCharString(buf), Name);
    return true;
  } else if (i >= dup->Maxbmp) {
    snprintf(g->Message, sizeof(g->Message), MSG(OPT_LOGIC_ERR), i);
    return true;
  } else {
    m = i / MAXBMP;
#if defined(_DEBUG)
    assert (m < Nbm);
#endif   // _DEBUG
    bmp[m] |= (1 << (i % MAXBMP));
  } // endif's i

  return false;
  } // end of SetBitMap

/***********************************************************************/
/*  Checks whether a column declared as sorted is sorted indeed.       */
/***********************************************************************/
bool DOSCOL::CheckSorted(PGLOBAL g)
  {
  if (Sorted)
  {
    if (OldVal) {
      // Verify whether this column is sorted all right
      if (OldVal->CompareValue(Value) > 0) {
        // Column is no more in ascending order
        snprintf(g->Message, sizeof(g->Message), MSG(COL_NOT_SORTED), Name, To_Tdb->GetName());
        Sorted = false;
        return true;
      } else
        OldVal->SetValue_pval(Value);

    } else
      OldVal = AllocateValue(g, Value);
  }
  return false;
  } // end of CheckSorted

/***********************************************************************/
/*  AddDistinctValue: Check whether this value already exist in the    */
/*  list and if not add it to the distinct values list.                */
/***********************************************************************/
bool DOSCOL::AddDistinctValue(PGLOBAL g)
  {
  bool found = false;
  int  i, m, n;

  ReadColumn(g);           // Extract column value from current line

  // Perhaps a better algorithm can be used when Ndv gets bigger
  // Here we cannot use Find because we must get the index of where
  // to insert a new value if it is not found in the array.
  for (n = 0; n < Ndv; n++) {
    m = Dval->CompVal(Value, n);

    if (m > 0)
      continue;
    else if (!m)
      found = true;        // Already there

    break;
    } // endfor n

  if (!found) {
    // Check whether we have room for an additional value
    if (Ndv == Freq) {
      // Too many values because of wrong Freq setting
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_FREQ_SET), Name);
      return true;
      } // endif Ndv

    // New value, add it to the list before the nth value
    Dval->SetNval(Ndv + 1);

    for (i = Ndv; i > n; i--)
      Dval->Move(i - 1, i);

    Dval->SetValue(Value, n);
    Ndv++;
    } // endif found

  return false;
  } // end of AddDistinctValue

/* ------------------------------------------------------------------- */

Messung V0.5 in Prozent
C=71 H=91 G=81

¤ Dauer der Verarbeitung: 0.66 Sekunden  (vorverarbeitet am  2026-10-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126864
#Domains=1897691