/************* TabMul C++ Program Source Code File (.CPP) **************/
/* PROGRAM NAME: TABMUL                                                */
/* -------------                                                       */
/*  Version 1.9                                                        */
/*                                                                     */
/* COPYRIGHT:                                                          */
/* ----------                                                          */
/*  (C) Copyright to PlugDB Software Development          2003 - 2017  */
/*  Author: Olivier BERTRAND                                           */
/*                                                                     */
/* WHAT THIS PROGRAM DOES:                                             */
/* -----------------------                                             */
/*  This program are the TDBMUL class DB routines.                     */
/*                                                                     */
/* WHAT YOU NEED TO COMPILE THIS PROGRAM:                              */
/* --------------------------------------                              */
/*                                                                     */
/*  REQUIRED FILES:                                                    */
/*  ---------------                                                    */
/*    TABMUL.CPP     - Source code                                     */
/*    PLGDBSEM.H     - DB application declaration file                 */
/*    TABDOS.H       - TABDOS classes declaration file                 */
/*    TABMUL.H       - TABFIX classes declaration file                 */
/*    GLOBAL.H       - Global declaration file                         */
/*                                                                     */
/*  REQUIRED LIBRARIES:                                                */
/*  -------------------                                                */
/*    Large model C library                                            */
/*                                                                     */
/*  REQUIRED PROGRAMS:                                                 */
/*  ------------------                                                 */
/*    IBM, Borland, GNU or Microsoft C++ Compiler and Linker           */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/*  Include relevant section of system dependant header files.         */
/***********************************************************************/
#include "my_global.h"
#if defined(__WIN__)
#include <stdlib.h>
#include <stdio.h>
#if defined(__BORLANDC__)
#define __MFC_COMPAT__                   // To define min/max as macro
#endif
//#include <windows.h>
#if defined(PATHMATCHSPEC) 
#include "Shlwapi.h"
//using namespace std;
#pragma comment(lib,"shlwapi.lib")
#endif   //	PATHMATCHSPEC
#else
#if defined(UNIX)
#include <fnmatch.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "osutil.h"
#else
//#include <io.h>
#endif
//#include <fcntl.h>
#endif

/***********************************************************************/
/*  Include application header files:                                  */
/***********************************************************************/
#include "global.h"      // global declarations
#include "plgdbsem.h"    // DB application declarations
#include "reldef.h"      // DB definition declares
#include "filamtxt.h"
#include "tabdos.h"      // TDBDOS and DOSCOL class dcls
#include "tabmul.h"      // TDBMUL and MULCOL classes dcls

/* ------------------------- Class TDBMUL ---------------------------- */

/***********************************************************************/
/*  TABMUL constructors.                                               */
/***********************************************************************/
TDBMUL::TDBMUL(PTDB tdbp) : TDBASE(tdbp->GetDef())
  {
  Tdbp = tdbp;
  Filenames = NULL;
  Rows = 0;
  Mul = tdbp->GetDef()->GetMultiple();
  NumFiles = 0;
  iFile = 0;
  } // end of TDBMUL standard constructor

TDBMUL::TDBMUL(PTDBMUL tdbp) : TDBASE(tdbp)
  {
  Tdbp = tdbp->Tdbp;
  Filenames = tdbp->Filenames;
  Rows = tdbp->Rows;
  Mul = tdbp->Mul;
  NumFiles = tdbp->NumFiles;
  iFile = tdbp->iFile;
  } // end of TDBMUL copy constructor

// Method
PTDB TDBMUL::Clone(PTABS t)
  {
  PTDBMUL tp;
  PGLOBAL g = t->G;        // Is this really useful ???

  tp = new(g) TDBMUL(this);
  tp->Tdbp = Tdbp->Clone(t);
  tp->Columns = tp->Tdbp->GetColumns();
  return tp;
  } // end of Clone

PTDB TDBMUL::Duplicate(PGLOBAL g)
  {
  PTDBMUL tmup = new(g) TDBMUL(this);

  tmup->Tdbp = Tdbp->Duplicate(g);
  return tmup;
  } // end of Duplicate

/***********************************************************************/
/*  Initializes the table filename list.                               */
/*  Note: tables created by concatenating the file components without  */
/*  specifying the LRECL value (that should be restricted to _MAX_PATH)*/
/*  have a LRECL that is the sum of the lengths of all components.     */
/*  This is why we use a big filename array to take care of that.      */
/***********************************************************************/
bool TDBMUL::InitFileNames(PGLOBAL g)
  {
#define PFNZ  4096
#define FNSZ  (_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT)
	PTDBDIR dirp;
	PSZ     pfn[PFNZ];
  PSZ     filename;
  int     rc, n = 0;

  if (trace)
    htrc("in InitFileName: fn[]=%d\n", FNSZ);

  filename = (char*)PlugSubAlloc(g, NULL, FNSZ);

  // The sub table may need to refer to the Table original block
  Tdbp->SetTable(To_Table);         // Was not set at construction

  PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath());

  if (trace)
    htrc("InitFileName: fn='%s'\n", filename);

  if (Mul != 2) {
    /*******************************************************************/
    /*  To_File is a multiple name with special characters             */
    /*******************************************************************/
		if (Mul == 1)
			dirp = new(g) TDBDIR(PlugDup(g, filename));
		else // Mul == 3 (Subdir)
		  dirp = new(g) TDBSDR(PlugDup(g, filename));

		if (dirp->OpenDB(g))
			return true;

		if (trace && Mul == 3) {
			int nf = ((PTDBSDR)dirp)->FindInDir(g);
			htrc("Number of files = %d\n", nf);
		} // endif trace

		while (true)
			if ((rc = dirp->ReadDB(g)) == RC_OK) {
#if defined(__WIN__)
				strcat(strcpy(filename, dirp->Drive), dirp->Direc);
#else   // !__WIN__
				strcpy(filename, dirp->Direc);
#endif  // !__WIN__
				strcat(strcat(filename, dirp->Fname), dirp->Ftype);
				pfn[n++] = PlugDup(g, filename);
			} else
				break;

		dirp->CloseDB(g);

		if (rc == RC_FX)
			return true;

  } else {
    /*******************************************************************/
    /*  To_File is the name of a file containing the file name list    */
    /*******************************************************************/
    char *p;
    FILE *stream;

    if (!(stream= global_fopen(g, MSGID_OPEN_MODE_STRERROR, filename, "r")))
      return true;

    while (n < PFNZ) {
      if (!fgets(filename, FNSZ, stream)) {
        fclose(stream);
        break;
        } // endif fgets

      p = filename + strlen(filename) - 1;

#if !defined(__WIN__)
      // Data files can be imported from Windows (having CRLF)
      if (*p == '\n' || *p == '\r') {
        // is this enough for Unix ???
        *p--;          // Eliminate ending CR or LF character

        if (p >= filename)
          // is this enough for Unix ???
          if (*p == '\n' || *p == '\r')
            *p--;    // Eliminate ending CR or LF character

        } // endif p

#else
      if (*p == '\n')
        p--;        // Eliminate ending new-line character
#endif
      // Trim rightmost blanks
      for (; p >= filename && *p == ' '; p--) ;

      *(++p) = '\0';

      // Suballocate the file name
      pfn[n++] = PlugDup(g, filename);
    } // endfor n

  } // endif Mul

  if (n) {
    Filenames = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));

    for (int i = 0; i < n; i++)
      Filenames[i] = pfn[i];

  } else {
    Filenames = (char**)PlugSubAlloc(g, NULL, sizeof(char*));
    Filenames[0] = NULL;
  } // endif n

  NumFiles = n;
  return false;
  } // end of InitFileNames

/***********************************************************************/
/*  The table column list is the sub-table column list.                */
/***********************************************************************/
PCOL TDBMUL::ColDB(PGLOBAL g, PSZ name, int num)
  {
  PCOL cp;

  /*********************************************************************/
  /*  Because special columns are directly added to the MUL block,     */
  /*  make sure that the sub-table has the same column list, before    */
  /*  and after the call to the ColDB function.                        */
  /*********************************************************************/
  Tdbp->SetColumns(Columns);
  cp = Tdbp->ColDB(g, name, num);
  Columns = Tdbp->GetColumns();
  return cp;
} // end of ColDB

/***********************************************************************/
/*  MUL GetProgMax: get the max value for progress information.        */
/***********************************************************************/
int TDBMUL::GetProgMax(PGLOBAL g)
  {
  if (!Filenames && InitFileNames(g))
    return -1;

  return NumFiles;                // This is a temporary setting
  } // end of GetProgMax

/***********************************************************************/
/*  MUL GetProgCur: get the current value for progress information.    */
/***********************************************************************/
int TDBMUL::GetProgCur(void)
  {
  return iFile;                   // This is a temporary setting
  } // end of GetProgMax

/***********************************************************************/
/*  MUL 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).          */
/*  Can be used on Multiple FIX table only.                            */
/***********************************************************************/
int TDBMUL::Cardinality(PGLOBAL g)
  {
  if (!g)
    return Tdbp->Cardinality(g);

  if (!Filenames && InitFileNames(g))
    return -1;

  int n, card = 0;

  for (int i = 0; i < NumFiles; i++) {
    Tdbp->SetFile(g, Filenames[i]);
    Tdbp->ResetSize();

    if ((n = Tdbp->Cardinality(g)) < 0) {
//    strcpy(g->Message, MSG(BAD_CARDINALITY));
      return -1;
      } // endif n

    card += n;
    } // endfor i

  return card;
  } // end of Cardinality

/***********************************************************************/
/*  Sum up the sizes of all sub-tables.                                */
/***********************************************************************/
int TDBMUL::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    int i;
    int mxsz;

    if (trace)
      htrc("TDBMUL::GetMaxSize: Filenames=%p\n", Filenames);
    
    if (!Filenames && InitFileNames(g))
      return -1;

    if (Use == USE_OPEN) {
      strcpy(g->Message, MSG(MAXSIZE_ERROR));
      return -1;
    } else
      MaxSize = 0;

    for (i = 0; i < NumFiles; i++) {
      Tdbp->SetFile(g, Filenames[i]);
      Tdbp->ResetSize();

      if ((mxsz = Tdbp->GetMaxSize(g)) < 0) {
        MaxSize = -1;
        return mxsz;
        } // endif mxsz

      MaxSize += mxsz;
      } // endfor i

    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  Reset read/write position values.                                  */
/***********************************************************************/
void TDBMUL::ResetDB(void)
  {
  for (PCOL colp = Columns; colp; colp = colp->GetNext())
    if (colp->GetAmType() == TYPE_AM_FILID)
      colp->COLBLK::Reset();

  Tdbp->ResetDB();
  } // end of ResetDB

/***********************************************************************/
/*  Returns RowId if b is false or Rownum if b is true.                */
/***********************************************************************/
int TDBMUL::RowNumber(PGLOBAL g, bool b)
  {
  return ((b) ? 0 : Rows)
       + ((iFile < NumFiles) ? Tdbp->RowNumber(g, b) : 1);
  } // end of RowNumber

/***********************************************************************/
/*  MUL Access Method opening routine.                                 */
/*  Open first file, other will be opened sequencially when reading.   */
/***********************************************************************/
bool TDBMUL::OpenDB(PGLOBAL g)
  {
  if (trace)
    htrc("MUL OpenDB: tdbp=%p tdb=R%d use=%d key=%p mode=%d\n",
      this, Tdb_No, Use, To_Key_Col, Mode);

  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open, replace it at its beginning.               */
    /*******************************************************************/
    if (Filenames[iFile = 0]) {
      Tdbp->CloseDB(g);
      Tdbp->SetUse(USE_READY);
      Tdbp->SetFile(g, Filenames[iFile = 0]);
      Tdbp->ResetSize();
      Rows = 0;
      ResetDB();
      return Tdbp->OpenDB(g);  // Re-open with new file name
    } else
      return false;

    } // endif use

  /*********************************************************************/
  /*  We need to calculate MaxSize before opening the query.           */
  /*********************************************************************/
  if (GetMaxSize(g) < 0)
    return true;

  /*********************************************************************/
  /*  Open the first table file of the list.                           */
  /*********************************************************************/
//if (!Filenames && InitFileNames(g))     // was done in GetMaxSize
//  return true;

  if (Filenames[iFile = 0]) {
    Tdbp->SetFile(g, Filenames[0]);
    Tdbp->SetMode(Mode);
    Tdbp->ResetDB();
    Tdbp->ResetSize();

    if (Tdbp->OpenDB(g))
      return true;

    } // endif *Filenames

  Use = USE_OPEN;
  return false;
  } // end of OpenDB

/***********************************************************************/
/*  ReadDB: Data Base read routine for MUL access method.              */
/***********************************************************************/
int TDBMUL::ReadDB(PGLOBAL g)
  {
  int rc;

  if (NumFiles == 0)
    return RC_EF;
  else if (To_Kindex) {
    /*******************************************************************/
    /*  Reading is by an index table.                                  */
    /*******************************************************************/
    strcpy(g->Message, MSG(NO_INDEX_READ));
    rc = RC_FX;
  } else {
    /*******************************************************************/
    /*  Now start the reading process.                                 */
    /*******************************************************************/
   retry:
    rc = Tdbp->ReadDB(g);

    if (rc == RC_EF) {
      if (Tdbp->GetDef()->GetPseudo() & 1)
        // Total number of rows met so far
        Rows += Tdbp->RowNumber(g) - 1;

      if (++iFile < NumFiles) {
        /***************************************************************/
        /*  Continue reading from next table file.                     */
        /***************************************************************/
        Tdbp->CloseDB(g);
        Tdbp->SetUse(USE_READY);
        Tdbp->SetFile(g, Filenames[iFile]);
        Tdbp->ResetSize();
        ResetDB();

        if (Tdbp->OpenDB(g))     // Re-open with new file name
          return RC_FX;

        goto retry;
        } // endif iFile

    } else if (rc == RC_FX)
      strcat(strcat(strcat(g->Message, " ("), Tdbp->GetFile(g)), ")");

  } // endif To_Kindex

  return rc;
  } // end of ReadDB

/***********************************************************************/
/*  Data Base write routine for MUL access method.                     */
/***********************************************************************/
int TDBMUL::WriteDB(PGLOBAL g)
  {
  return Tdbp->WriteDB(g);
//  strcpy(g->Message, MSG(TABMUL_READONLY));
//  return RC_FX;                    // NIY
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for MUL access method.               */
/***********************************************************************/
int TDBMUL::DeleteDB(PGLOBAL g, int)
  {
  // When implementing DELETE_MODE InitFileNames must be updated to
  // eliminate CRLF under Windows if the file is read in binary.
  strcpy(g->Message, MSG(TABMUL_READONLY));
  return RC_FX;                                      // NIY
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for MUL access method.                     */
/***********************************************************************/
void TDBMUL::CloseDB(PGLOBAL g)
  {
  if (NumFiles > 0) {
    Tdbp->CloseDB(g);
    iFile = NumFiles;
    } // endif NumFiles

  } // end of CloseDB

#if 0
/* ------------------------- Class TDBMSD ---------------------------- */

	// Method
PTDB TDBMSD::Clone(PTABS t)
{
	PTDBMSD tp;
	PGLOBAL g = t->G;        // Is this really useful ???

	tp = new(g) TDBMSD(this);
	tp->Tdbp = Tdbp->Clone(t);
	tp->Columns = tp->Tdbp->GetColumns();
	return tp;
} // end of Clone

PTDB TDBMSD::Duplicate(PGLOBAL g)
{
	PTDBMSD tmup = new(g) TDBMSD(this);

	tmup->Tdbp = Tdbp->Duplicate(g);
	return tmup;
} // end of Duplicate

/***********************************************************************/
/*  Initializes the table filename list.                               */
/*  Note: tables created by concatenating the file components without  */
/*  specifying the LRECL value (that should be restricted to _MAX_PATH)*/
/*  have a LRECL that is the sum of the lengths of all components.     */
/*  This is why we use a big filename array to take care of that.      */
/***********************************************************************/
bool TDBMSD::InitFileNames(PGLOBAL g)
{
#define PFNZ  4096
#define FNSZ  (_MAX_DRIVE+_MAX_DIR+_MAX_FNAME+_MAX_EXT)
	PTDBSDR dirp;
	PSZ     pfn[PFNZ];
	PSZ     filename;
	int     rc, n = 0;

	if (trace)
		htrc("in InitFileName: fn[]=%d\n", FNSZ);

	filename = (char*)PlugSubAlloc(g, NULL, FNSZ);

	// The sub table may need to refer to the Table original block
	Tdbp->SetTable(To_Table);         // Was not set at construction

	PlugSetPath(filename, Tdbp->GetFile(g), Tdbp->GetPath());

	if (trace)
		htrc("InitFileName: fn='%s'\n", filename);

	dirp = new(g) TDBSDR(filename);

	if (dirp->OpenDB(g))
		return true;

	while (true)
		if ((rc = dirp->ReadDB(g)) == RC_OK) {
#if defined(__WIN__)
			strcat(strcpy(filename, dirp->Drive), dirp->Direc);
#else   // !__WIN__
			strcpy(filename, dirp->Direc);
#endif  // !__WIN__
			strcat(strcat(filename, dirp->Fname), dirp->Ftype);
			pfn[n++] = PlugDup(g, filename);
		} else
			break;

	if (rc == RC_FX)
		return true;

	if (n) {
	  Filenames = (char**)PlugSubAlloc(g, NULL, n * sizeof(char*));

	  for (int i = 0; i < n; i++)
		  Filenames[i] = pfn[i];

	} else {
	  Filenames = (char**)PlugSubAlloc(g, NULL, sizeof(char*));
	  Filenames[0] = NULL;
	} // endif n

	NumFiles = n;
	return false;
} // end of InitFileNames
#endif // 0

	/* --------------------------- Class DIRDEF -------------------------- */

/***********************************************************************/
/*  DefineAM: define specific AM block values from XDB file.           */
/***********************************************************************/
bool DIRDEF::DefineAM(PGLOBAL g, LPCSTR, int)
  {
  Desc = Fn = GetStringCatInfo(g, "Filename", NULL);
  Incl = GetBoolCatInfo("Subdir", false);
	Huge = GetBoolCatInfo("Huge", false);
	Nodir = GetBoolCatInfo("Nodir", true);
  return false;
  } // end of DefineAM

/***********************************************************************/
/*  GetTable: makes a new Table Description Block.                     */
/***********************************************************************/
PTDB DIRDEF::GetTable(PGLOBAL g, MODE)
  {
#if 0
  if (Huge)
    return new(g) TDBDHR(this);        // Not implemented yet
  else
#endif
  if (Incl)
    return new(g) TDBSDR(this);        // Including sub-directory files
  else
    return new(g) TDBDIR(this);    // Not Including sub-directory files

  } // end of GetTable

/* ------------------------- Class TDBDIR ---------------------------- */

/***********************************************************************/
/*  TABDIR constructors.                                               */
/***********************************************************************/
void TDBDIR::Init(void)
{
	iFile = 0;
#if defined(__WIN__)
	Dvalp = NULL;
	memset(&FileData, 0, sizeof(_finddata_t));
	hSearch = INVALID_HANDLE_VALUE;
	*Drive = '\0';
#else   // !__WIN__
	memset(&Fileinfo, 0, sizeof(struct stat));
	Entry = NULL;
	Dir = NULL;
	Done = false;
	*Pattern = '\0';
#endif  // !__WIN__
	*Fpath = '\0';
	*Direc = '\0';
	*Fname = '\0';
	*Ftype = '\0';
}	// end of Init

TDBDIR::TDBDIR(PDIRDEF tdp) : TDBASE(tdp)
{
  To_File = tdp->Fn;
	Nodir = tdp->Nodir;
	Init();
} // end of TDBDIR standard constructor

TDBDIR::TDBDIR(PSZ fpat) : TDBASE((PTABDEF)NULL)
{
	To_File = fpat;
	Nodir = true;
	Init();
} // end of TDBDIR constructor

/***********************************************************************/
/*  Initialize/get the components of the search file pattern.          */
/***********************************************************************/
char* TDBDIR::Path(PGLOBAL g)
  {
  PCATLG cat = PlgGetCatalog(g);
	PTABDEF defp = (PTABDEF)To_Def;

#if defined(__WIN__)
  if (!*Drive) {
    PlugSetPath(Fpath, To_File, defp ? defp->GetPath() : NULL);
    _splitpath(Fpath, Drive, Direc, Fname, Ftype);
  } else
    _makepath(Fpath, Drive, Direc, Fname, Ftype); // Usefull for TDBSDR

  return Fpath;
#else   // !__WIN__
  if (!Done) {
    PlugSetPath(Fpath, To_File, defp ? defp->GetPath() : NULL);
    _splitpath(Fpath, NULL, Direc, Fname, Ftype);
    strcat(strcpy(Pattern, Fname), Ftype);
    Done = true;
    } // endif Done

  return Pattern;
#endif  // !__WIN__
  } // end of Path

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

/***********************************************************************/
/*  DIR GetMaxSize: returns the number of retrieved files.             */
/***********************************************************************/
int TDBDIR::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    int rc, n = -1;
#if defined(__WIN__)

    // Start searching files in the target directory.
		hSearch = FindFirstFile(Path(g), &FileData);

    if (hSearch == INVALID_HANDLE_VALUE) {
			rc = GetLastError();

			if (rc != ERROR_FILE_NOT_FOUND) {
				char buf[512];

				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
					            FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
				sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
				return -1;
			} // endif rc

			return 0;
		} // endif hSearch

		while (true) {
			if (!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
				n++;

			if (!FindNextFile(hSearch, &FileData)) {
				rc = GetLastError();

				if (rc != ERROR_NO_MORE_FILES) {
					sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
					FindClose(hSearch);
					return -1;
				} // endif rc

				break;
			} // endif Next

		} // endwhile

    // Close the search handle.
		FindClose(hSearch);
#else   // !__WIN__
    Path(g);

    // Start searching files in the target directory.
    if (!(Dir = opendir(Direc))) {
      sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
      return -1;
      } // endif dir

    while ((Entry = readdir(Dir))) {
      strcat(strcpy(Fpath, Direc), Entry->d_name);

      if (lstat(Fpath, &Fileinfo) < 0) {
        sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
        return -1;
      } else if (S_ISREG(Fileinfo.st_mode))
        // Test whether the file name matches the table name filter
        if (!fnmatch(Pattern, Entry->d_name, 0))
          n++;      // We have a match

      } // endwhile Entry

    // Close the DIR handle.
    closedir(Dir);
#endif  // !__WIN__
    MaxSize = n;
    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  DIR Access Method opening routine.                                 */
/*  Open first file, other will be opened sequencially when reading.   */
/***********************************************************************/
bool TDBDIR::OpenDB(PGLOBAL g)
  {
  if (trace)
    htrc("DIR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
      this, Tdb_No, Use, Mode);

  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open, reopen it.                                 */
    /*******************************************************************/
    CloseDB(g);
    SetUse(USE_READY);
    } // endif use

  Use = USE_OPEN;
#if !defined(__WIN__)
  Path(g);                          // Be sure it is done
  Dir = NULL;                       // For ReadDB
#endif   // !__WIN__
  return false;
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for DIR access method.                      */
/***********************************************************************/
int TDBDIR::ReadDB(PGLOBAL g)
  {
  int rc = RC_OK;

#if defined(__WIN__)
	do {
		if (hSearch == INVALID_HANDLE_VALUE) {
			/*****************************************************************/
			/*  Start searching files in the target directory. The use of    */
			/*  the Path function is required when called from TDBSDR.       */
			/*****************************************************************/
			hSearch = FindFirstFile(Path(g), &FileData);

			if (hSearch == INVALID_HANDLE_VALUE) {
				rc = RC_EF;
				break;
			} else
				iFile++;

		} else {
			if (!FindNextFile(hSearch, &FileData)) {
				// Restore file name and type pattern
				_splitpath(To_File, NULL, NULL, Fname, Ftype);
				rc = RC_EF;
				break;
			} else
				iFile++;

		} // endif hSearch

	} while (Nodir && FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);

  if (rc == RC_OK)
    _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype);

#else   // !Win32
  rc = RC_NF;

  if (!Dir)
    // Start searching files in the target directory.
    if (!(Dir = opendir(Direc))) {
      sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
      rc = RC_FX;
      } // endif dir

  while (rc == RC_NF)
    if ((Entry = readdir(Dir))) {
      // We need the Fileinfo structure to get info about the file
      strcat(strcpy(Fpath, Direc), Entry->d_name);

      if (lstat(Fpath, &Fileinfo) < 0) {
        sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
        rc = RC_FX;
      } else if (S_ISREG(Fileinfo.st_mode))
        // Test whether the file name matches the table name filter
        if (!fnmatch(Pattern, Entry->d_name, 0)) {
          iFile++;      // We have a match
          _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype);
          rc = RC_OK;
          } // endif fnmatch

    } else {
      // Restore file name and type pattern
      _splitpath(To_File, NULL, NULL, Fname, Ftype);
      rc = RC_EF;
    } // endif Entry

#endif  // !__WIN__

  return rc;
  } // end of ReadDB

/***********************************************************************/
/*  Data Base write routine for DIR access method.                     */
/***********************************************************************/
int TDBDIR::WriteDB(PGLOBAL g)
  {
  strcpy(g->Message, MSG(TABDIR_READONLY));
  return RC_FX;                    // NIY
  } // end of WriteDB

/***********************************************************************/
/*  Data Base delete line routine for DIR access method.               */
/***********************************************************************/
int TDBDIR::DeleteDB(PGLOBAL g, int)
  {
  strcpy(g->Message, MSG(TABDIR_READONLY));
  return RC_FX;                                      // NIY
  } // end of DeleteDB

/***********************************************************************/
/*  Data Base close routine for MUL access method.                     */
/***********************************************************************/
void TDBDIR::CloseDB(PGLOBAL)
  {
#if defined(__WIN__)
  // Close the search handle.
  FindClose(hSearch);
	hSearch = INVALID_HANDLE_VALUE;
#else   // !__WIN__
  // Close the DIR handle
  if (Dir) {
    closedir(Dir);
    Dir = NULL;
    } // endif dir
#endif  // !__WIN__
  iFile = 0;
  } // end of CloseDB

// ------------------------ DIRCOL functions ----------------------------

/***********************************************************************/
/*  DIRCOL public constructor.                                         */
/***********************************************************************/
DIRCOL::DIRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ)
  : COLBLK(cdp, tdbp, i)
  {
  if (cprec) {
    Next = cprec->GetNext();
    cprec->SetNext(this);
  } else {
    Next = tdbp->GetColumns();
    tdbp->SetColumns(this);
  } // endif cprec

  // Set additional DIR access method information for column.
	Tdbp = (PTDBDIR)tdbp;
  N = cdp->GetOffset();
  } // end of DIRCOL constructor

/***********************************************************************/
/*  DIRCOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
DIRCOL::DIRCOL(DIRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
  {
	Tdbp = (PTDBDIR)tdbp;
	N = col1->N;
  } // end of DIRCOL copy constructor

#if defined(__WIN__)
/***********************************************************************/
/*  Retrieve time information from FileData.                           */
/***********************************************************************/
void DIRCOL::SetTimeValue(PGLOBAL g, FILETIME& ftime)
{
	char       tsp[24];
	SYSTEMTIME stp;

	if (FileTimeToSystemTime(&ftime, &stp)) {
		sprintf(tsp, "%04d-%02d-%02d %02d:%02d:%02d",
			stp.wYear, stp.wMonth, stp.wDay, stp.wHour, stp.wMinute, stp.wSecond);

		if (Value->GetType() != TYPE_STRING) {
			if (!Tdbp->Dvalp)
				Tdbp->Dvalp = AllocateValue(g, TYPE_DATE, 20, 0, false,
					"YYYY-MM-DD hh:mm:ss");

			Tdbp->Dvalp->SetValue_psz(tsp);
			Value->SetValue_pval(Tdbp->Dvalp);
		} else
			Value->SetValue_psz(tsp);

	} else
		Value->Reset();

} // end of SetTimeValue
#endif   // __WIN__

/***********************************************************************/
/*  ReadColumn: what this routine does is to access the information    */
/*  corresponding to this column and convert it to buffer type.        */
/***********************************************************************/
void DIRCOL::ReadColumn(PGLOBAL g)
	{
  if (trace)
    htrc("DIR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n",
      Name, Tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N);

  /*********************************************************************/
  /*  Retrieve the information corresponding to the column number.     */
  /*********************************************************************/
  switch (N) {
#if defined(__WIN__)
    case  0: Value->SetValue_psz(Tdbp->Drive); break;
#endif   // __WIN__
    case  1: Value->SetValue_psz(Tdbp->Direc); break;
    case  2: Value->SetValue_psz(Tdbp->Fname); break;
    case  3: Value->SetValue_psz(Tdbp->Ftype); break;
#if defined(__WIN__)
    case  4: Value->SetValue((int)Tdbp->FileData.dwFileAttributes); break;
		case  5: Value->SetValue((int)Tdbp->FileData.nFileSizeLow); break;
    case  6: SetTimeValue(g, Tdbp->FileData.ftLastWriteTime);   break;
    case  7: SetTimeValue(g, Tdbp->FileData.ftCreationTime);    break;
    case  8: SetTimeValue(g, Tdbp->FileData.ftLastAccessTime);  break;
#else   // !__WIN__
    case  4: Value->SetValue((int)Tdbp->Fileinfo.st_mode);  break;
    case  5: Value->SetValue((int)Tdbp->Fileinfo.st_size);  break;
    case  6: Value->SetValue((int)Tdbp->Fileinfo.st_mtime); break;
    case  7: Value->SetValue((int)Tdbp->Fileinfo.st_ctime); break;
    case  8: Value->SetValue((int)Tdbp->Fileinfo.st_atime); break;
    case  9: Value->SetValue((int)Tdbp->Fileinfo.st_uid);   break;
    case 10: Value->SetValue((int)Tdbp->Fileinfo.st_gid);   break;
#endif  // !__WIN__
    default:
      sprintf(g->Message, MSG(INV_DIRCOL_OFST), N);
			throw GetAmType();
	} // endswitch N

  } // end of ReadColumn

/* ------------------------- Class TDBSDR ---------------------------- */

/***********************************************************************/
/*  SDR GetMaxSize: returns the number of retrieved files.             */
/***********************************************************************/
int TDBSDR::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    Path(g);
    MaxSize = FindInDir(g);
    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  SDR FindInDir: returns the number of retrieved files.              */
/***********************************************************************/
int TDBSDR::FindInDir(PGLOBAL g)
  {
  int    rc, n = 0;
  size_t m = strlen(Direc);

  // Start searching files in the target directory.
#if defined(__WIN__)
	HANDLE h;

#if defined(PATHMATCHSPEC)
	if (!*Drive)
		Path(g);

	_makepath(Fpath, Drive, Direc, "*", "*");

	h = FindFirstFile(Fpath, &FileData);

  if (h == INVALID_HANDLE_VALUE) {
		rc = GetLastError();

		if (rc != ERROR_FILE_NOT_FOUND) {
			char buf[512];

			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
				FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
			return -1;
		} // endif rc

		return 0;
	} // endif h

	while (true) {
		if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
			  *FileData.cFileName != '.') {
			// Look in the name sub-directory
			strcat(strcat(Direc, FileData.cFileName), "/");
			n += FindInDir(g);
			Direc[m] = '\0';         // Restore path
		} else if (PathMatchSpec(FileData.cFileName, Fpath))
			n++;

		if (!FindNextFile(h, &FileData)) {
			rc = GetLastError();

			if (rc != ERROR_NO_MORE_FILES) {
				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
				FindClose(h);
				return -1;
			} // endif rc

			break;
		} // endif Next

	} // endwhile
#else   // !PATHMATCHSPEC
	h = FindFirstFile(Path(g), &FileData);

	if (h == INVALID_HANDLE_VALUE) {
		rc = GetLastError();

		if (rc != ERROR_FILE_NOT_FOUND) {
			char buf[512];

			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
				FORMAT_MESSAGE_IGNORE_INSERTS,
				NULL, GetLastError(), 0, (LPTSTR)&buf, sizeof(buf), NULL);
			sprintf(g->Message, MSG(BAD_FILE_HANDLE), buf);
			return -1;
		} // endif rc

		return 0;
	} // endif hSearch

	while (true) {
		n++;

		if (!FindNextFile(h, &FileData)) {
			rc = GetLastError();

			if (rc != ERROR_NO_MORE_FILES) {
				sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
				FindClose(h);
				return -1;
			} // endif rc

			break;
		} // endif Next

	} // endwhile

	// Now search files in sub-directories.
	_makepath(Fpath, Drive, Direc, "*", ".");
	h = FindFirstFile(Fpath, &FileData);

	if (h != INVALID_HANDLE_VALUE) {
		while (true) {
			if ((FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
				  *FileData.cFileName != '.') {
				// Look in the name sub-directory
				strcat(strcat(Direc, FileData.cFileName), "/");
				n += FindInDir(g);
				Direc[m] = '\0';         // Restore path
			} // endif SUBDIR

			if (!FindNextFile(h, &FileData))
				break;

		} // endwhile

	} // endif h
#endif  // !PATHMATCHSPEC

  // Close the search handle.
	FindClose(h);
#else   // !__WIN__
  int k;
  DIR *dir = opendir(Direc);

  if (!dir) {
    sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
    return -1;
    } // endif dir

  while ((Entry = readdir(dir))) {
    strcat(strcpy(Fpath, Direc), Entry->d_name);

    if (lstat(Fpath, &Fileinfo) < 0) {
      sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
      return -1;
    } else if (S_ISDIR(Fileinfo.st_mode) && *Entry->d_name != '.') {
      // Look in the name sub-directory
      strcat(strcat(Direc, Entry->d_name), "/");

      if ((k = FindInDir(g)) < 0)
        return k;
      else
        n += k;

      Direc[m] = '\0';         // Restore path
    } else if (S_ISREG(Fileinfo.st_mode))
      // Test whether the file name matches the table name filter
      if (!fnmatch(Pattern, Entry->d_name, 0))
        n++;      // We have a match

    } // endwhile readdir

  // Close the DIR handle.
  closedir(dir);
#endif  // !__WIN__

  return n;
  } // end of FindInDir

/***********************************************************************/
/*  DIR Access Method opening routine.                                 */
/*  Open first file, other will be opened sequencially when reading.   */
/***********************************************************************/
bool TDBSDR::OpenDB(PGLOBAL g)
  {
  if (!Sub) {
    Path(g);
    Sub = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
    Sub->Next = NULL;
    Sub->Prev = NULL;
#if defined(__WIN__)
    Sub->H = INVALID_HANDLE_VALUE;
    Sub->Len = strlen(Direc);
#else   // !__WIN__
    Sub->D = NULL;
    Sub->Len = 0;
#endif  // !__WIN__
    } // endif To_Sub

  return TDBDIR::OpenDB(g);
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for SDR access method.                      */
/***********************************************************************/
int TDBSDR::ReadDB(PGLOBAL g)
  {
  int rc;

#if defined(__WIN__)
 again:
  rc = TDBDIR::ReadDB(g);

  if (rc == RC_EF) {
    // Are there more files in sub-directories
   retry:
    do {
      if (Sub->H == INVALID_HANDLE_VALUE) {
//      _makepath(Fpath, Drive, Direc, "*", ".");		 why was this made?
				_makepath(Fpath, Drive, Direc, "*", NULL);
				Sub->H = FindFirstFile(Fpath, &FileData);
      } else if (!FindNextFile(Sub->H, &FileData)) {
        FindClose(Sub->H);
        Sub->H = INVALID_HANDLE_VALUE;
        *FileData.cFileName= '\0';
				break;
      } // endif findnext

    } while(!(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
    		(*FileData.cFileName == '.' && 
			  (!FileData.cFileName[1] || FileData.cFileName[1] == '.')));

    if (Sub->H == INVALID_HANDLE_VALUE) {
      // No more sub-directories. Are we in a sub-directory?
      if (!Sub->Prev)
        return rc;               // No, all is finished

      // here we must continue in the parent directory
      Sub = Sub->Prev;
      goto retry;
    } else {
      // Search next sub-directory
      Direc[Sub->Len] = '\0';

      if (!Sub->Next) {
        PSUBDIR sup;

        sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
        sup->Next = NULL;
        sup->Prev = Sub;
        sup->H = INVALID_HANDLE_VALUE;
        Sub->Next = sup;
        } // endif Next

      Sub = Sub->Next;
      strcat(strcat(Direc, FileData.cFileName), "/");
      Sub->Len = strlen(Direc);

      // Reset Hsearch used by TDBDIR::ReadDB
			FindClose(hSearch);
			hSearch = INVALID_HANDLE_VALUE;
      goto again;
    } // endif H

    } // endif rc
#else   // !__WIN__
  rc = RC_NF;

 again:
  if (!Sub->D)
    // Start searching files in the target directory.
    if (!(Sub->D = opendir(Direc))) {
      sprintf(g->Message, MSG(BAD_DIRECTORY), Direc, strerror(errno));
      rc = RC_FX;
      } // endif dir

  while (rc == RC_NF)
    if ((Entry = readdir(Sub->D))) {
      // We need the Fileinfo structure to get info about the file
      strcat(strcpy(Fpath, Direc), Entry->d_name);

      if (lstat(Fpath, &Fileinfo) < 0) {
        sprintf(g->Message, "%s: %s", Fpath, strerror(errno));
        rc = RC_FX;
      } else if (S_ISDIR(Fileinfo.st_mode) && strcmp(Entry->d_name, ".")
			                                     && strcmp(Entry->d_name, "..")) {
        // Look in the name sub-directory
        if (!Sub->Next) {
          PSUBDIR sup;

          sup = (PSUBDIR)PlugSubAlloc(g, NULL, sizeof(SUBDIR));
          sup->Next = NULL;
          sup->Prev = Sub;
          Sub->Next = sup;
          } // endif Next

        Sub = Sub->Next;
        Sub->D = NULL;
        Sub->Len = strlen(Direc);
        strcat(strcat(Direc, Entry->d_name), "/");
        goto again;
      } else if (S_ISREG(Fileinfo.st_mode))
        // Test whether the file name matches the table name filter
        if (!fnmatch(Pattern, Entry->d_name, 0)) {
          iFile++;      // We have a match
          _splitpath(Entry->d_name, NULL, NULL, Fname, Ftype);
          rc = RC_OK;
          } // endif fnmatch

    } else {
      // No more files. Close the DIR handle.
      closedir(Sub->D);

      // Are we in a sub-directory?
      if (Sub->Prev) {
        // Yes, we must continue in the parent directory
        Direc[Sub->Len] = '\0';
        Sub = Sub->Prev;
      } else
        rc = RC_EF;              // No, all is finished

    } // endif Entry

#endif  // !__WIN__

  return rc;
  } // end of ReadDB

#if 0
/* ------------------------- Class TDBDHR ---------------------------- */

/***********************************************************************/
/*  TABDHR constructors.                                               */
/***********************************************************************/
TDBDHR::TDBDHR(PDHRDEF tdp) : TDBASE(tdp)
  {
  memset(&FileData, 0, sizeof(WIN32_FIND_DATA));
  Hsearch = INVALID_HANDLE_VALUE;
  iFile = 0;
  *Drive = '\0';
  *Direc = '\0';
  *Fname = '\0';
  *Ftype = '\0';
  } // end of TDBDHR standard constructor

TDBDHR::TDBDHR(PTDBDHR tdbp) : TDBASE(tdbp)
  {
  FileData = tdbp->FileData;
  Hsearch = tdbp->Hsearch;
  iFile = tdbp->iFile;
  strcpy(Drive, tdbp->Drive);
  strcpy(Direc, tdbp->Direc);
  strcpy(Fname, tdbp->Fname);
  strcpy(Ftype, tdbp->Ftype);
  } // end of TDBDHR copy constructor

// Method
PTDB TDBDHR::Clone(PTABS t)
  {
  PTDB    tp;
  PGLOBAL g = t->G;        // Is this really useful ???

  tp = new(g) TDBDHR(this);
  tp->Columns = Columns;
  return tp;
  } // end of Clone

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

/***********************************************************************/
/*  DHR GetMaxSize: returns the number of retrieved files.             */
/***********************************************************************/
int TDBDHR::GetMaxSize(PGLOBAL g)
  {
  if (MaxSize < 0) {
    char    filename[_MAX_PATH];
    int     i, rc;
    int    n = -1;
    HANDLE  h;
    PDBUSER dup = PlgGetUser(g);

    PlugSetPath(filename, To_File, dup->Path);

    // Start searching files in the target directory.
    h = FindFirstFile(filename, &FileData);

    if (h == INVALID_HANDLE_VALUE) {
      switch (rc = GetLastError()) {
        case ERROR_NO_MORE_FILES:
        case ERROR_FILE_NOT_FOUND:
          n = 0;
          break;
        default:
          FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                        FORMAT_MESSAGE_IGNORE_INSERTS,
                        NULL, rc, 0,
                        (LPTSTR)&filename, sizeof(filename), NULL);
          sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
        } // endswitch rc

    } else {
      for (n = 1;; n++)
        if (!FindNextFile(h, &FileData)) {
          rc = GetLastError();

          if (rc != ERROR_NO_MORE_FILES) {
            sprintf(g->Message, MSG(NEXT_FILE_ERROR), rc);
            n = -1;
            } // endif rc

          break;
          } // endif FindNextFile

      // Close the search handle.
      if (!FindClose(h) && n != -1)
        strcpy(g->Message, MSG(SRCH_CLOSE_ERR));

    } // endif Hsearch

    MaxSize = n;
    } // endif MaxSize

  return MaxSize;
  } // end of GetMaxSize

/***********************************************************************/
/*  DHR Access Method opening routine.                                 */
/*  Open first file, other will be opened sequencially when reading.   */
/***********************************************************************/
bool TDBDHR::OpenDB(PGLOBAL g)
  {
  if (trace)
    htrc("DHR OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
      this, Tdb_No, Use, Mode);

  if (Use == USE_OPEN) {
    /*******************************************************************/
    /*  Table already open, reopen it.                                 */
    /*******************************************************************/
    CloseDB(g);
    SetUse(USE_READY);
    } // endif use

  /*********************************************************************/
  /*  Direct access needed for join or sorting.                        */
  /*********************************************************************/
  if (NeedIndexing(g)) {
    // Direct access of DHR tables is not implemented yet
    sprintf(g->Message, MSG(NO_DIR_INDX_RD), "DHR");
    return true;
    } // endif NeedIndexing

  Use = USE_OPEN;
  return false;
  } // end of OpenDB

/***********************************************************************/
/*  Data Base read routine for DHR access method.                      */
/***********************************************************************/
int TDBDHR::ReadDB(PGLOBAL g)
  {
  int   rc = RC_OK;
  DWORD erc;

  if (Hsearch == INVALID_HANDLE_VALUE) {
    char   *filename[_MAX_PATH];
    PDBUSER dup = PlgGetUser(g);

    PlugSetPath(filename, To_File, dup->Path);
    _splitpath(filename, Drive, Direc, NULL, NULL);

    /*******************************************************************/
    /*  Start searching files in the target directory.                 */
    /*******************************************************************/
    Hsearch = FindFirstFile(filename, &FileData);

    if (Hsearch != INVALID_HANDLE_VALUE)
      iFile = 1;
    else switch (erc = GetLastError()) {
      case ERROR_NO_MORE_FILES:
      case ERROR_FILE_NOT_FOUND:
//    case ERROR_PATH_NOT_FOUND:               ???????
        rc = RC_EF;
        break;
      default:
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL, erc,  0,
                      (LPTSTR)&filename, sizeof(filename), NULL);
        sprintf(g->Message, MSG(BAD_FILE_HANDLE), filename);
        rc = RC_FX;
      } // endswitch erc

  } else {
    if (!FindNextFile(Hsearch, &FileData)) {
      DWORD erc = GetLastError();

      if (erc != ERROR_NO_MORE_FILES) {
        sprintf(g->Message, MSG(NEXT_FILE_ERROR), erc);
        FindClose(Hsearch);
        rc = RC_FX;
      } else
        rc = RC_EF;

    } else
      iFile++;

  } // endif Hsearch

  if (rc == RC_OK)
    _splitpath(FileData.cFileName, NULL, NULL, Fname, Ftype);

  return rc;
  } // end of ReadDB

/***********************************************************************/
/*  Data Base close routine for MUL access method.                     */
/***********************************************************************/
void TDBDHR::CloseDB(PGLOBAL g)
  {
  // Close the search handle.
  if (!FindClose(Hsearch)) {
    strcpy(g->Message, MSG(SRCH_CLOSE_ERR));
		throw GetAmType();
	} // endif FindClose

  iFile = 0;
  Hsearch = INVALID_HANDLE_VALUE;
  } // end of CloseDB

// ------------------------ DHRCOL functions ----------------------------

/***********************************************************************/
/*  DHRCOL public constructor.                                         */
/***********************************************************************/
DHRCOL::DHRCOL(PCOLDEF cdp, PTDB tdbp, PCOL cprec, int i, PCSZ am)
      : COLBLK(cdp, tdbp, i)
  {
  if (cprec) {
    Next = cprec->GetNext();
    cprec->SetNext(this);
  } else {
    Next = tdbp->GetColumns();
    tdbp->SetColumns(this);
  } // endif cprec

  // Set additional DHR access method information for column.
  N = cdp->GetOffset();
  } // end of DOSCOL constructor

/***********************************************************************/
/*  DHRCOL constructor used for copying columns.                       */
/*  tdbp is the pointer to the new table descriptor.                   */
/***********************************************************************/
DHRCOL::DHRCOL(DHRCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
  {
  N = col1->N;
  } // end of DHRCOL copy constructor

/***********************************************************************/
/*  ReadColumn: what this routine does is to access the information    */
/*  corresponding to this column and convert it to buffer type.        */
/***********************************************************************/
void DHRCOL::ReadColumn(PGLOBAL g)
  {
  int     rc;
  PTDBDHR tdbp = (PTDBDHR)To_Tdb;

  if (trace)
    htrc("DHR ReadColumn: col %s R%d use=%.4X status=%.4X type=%d N=%d\n",
      Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type, N);

  /*********************************************************************/
  /*  Retrieve the information corresponding to the column number.     */
  /*********************************************************************/
  switch (N) {
    case 0:                                // Drive
      Value->SetValue(Drive, _MAX_DRIVE);
      break;
    case 1:                                // Path
      Value->SetValue(Direc, _MAX_DHR);
      break;
    case 2:                                // Name
      Value->SetValue(Fname, _MAX_FNAME);
      break;
    case 3:                                // Extention
      Value->SetValue(Ftype, _MAX_EXT);
      break;
    case 4:                                // Extention
      Value->SetValue(tdbp->FileData.cAlternateFileName, 14);
      break;
    case 5:
      Value->SetValue(tdbp->FileData.dwFileAttributes);
      break;
    case 6:
      Value->SetValue(..................
  } // end of ReadColumn
#endif // 0

