mariadb/storage/connect/tabmul.cpp
Mikhail Chalov 19af1890b5 Use memory safe snprintf() in Connect Engine
This commit replaces sprintf(buf, ...) with
snprintf(buf, sizeof(buf), ...),
specifically in the "easy" cases where buf is allocated with a size
known at compile time.

The changes make sure we are not write outside array/string bounds which
will lead to undefined behaviour. In case the code is trying to write
outside bounds - safe version of functions simply cut the string
messages so we process this gracefully.

All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the BSD-new
license.  I am contributing on behalf of my employer Amazon Web Services,
Inc.

bsonudf.cpp warnings cleanup by Daniel Black

Reviewer: Daniel Black
2022-07-26 16:28:59 +10:00

1622 lines
50 KiB
C++

/************* 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(_WIN32)
#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(1))
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(1))
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(1) && 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(_WIN32)
strcat(strcpy(filename, dirp->Drive), dirp->Direc);
#else // !_WIN32
strcpy(filename, dirp->Direc);
#endif // !_WIN32
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(_WIN32)
// 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(1))
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(1))
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(1))
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(1))
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(_WIN32)
strcat(strcpy(filename, dirp->Drive), dirp->Direc);
#else // !_WIN32
strcpy(filename, dirp->Direc);
#endif // !_WIN32
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(_WIN32)
Dvalp = NULL;
memset(&FileData, 0, sizeof(_finddata_t));
hSearch = INVALID_HANDLE_VALUE;
*Drive = '\0';
#else // !_WIN32
memset(&Fileinfo, 0, sizeof(struct stat));
Entry = NULL;
Dir = NULL;
Done = false;
*Pattern = '\0';
#endif // !_WIN32
*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(_WIN32)
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 // !_WIN32
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 // !_WIN32
} // 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(_WIN32)
// 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);
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(g->Message), MSG(NEXT_FILE_ERROR), rc);
FindClose(hSearch);
return -1;
} // endif rc
break;
} // endif Next
} // endwhile
// Close the search handle.
FindClose(hSearch);
#else // !_WIN32
Path(g);
// Start searching files in the target directory.
if (!(Dir = opendir(Direc))) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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 // !_WIN32
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(1))
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(_WIN32)
Path(g); // Be sure it is done
Dir = NULL; // For ReadDB
#endif // !_WIN32
return false;
} // end of OpenDB
/***********************************************************************/
/* Data Base read routine for DIR access method. */
/***********************************************************************/
int TDBDIR::ReadDB(PGLOBAL g)
{
int rc = RC_OK;
#if defined(_WIN32)
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))) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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 // !_WIN32
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(_WIN32)
// Close the search handle.
FindClose(hSearch);
hSearch = INVALID_HANDLE_VALUE;
#else // !_WIN32
// Close the DIR handle
if (Dir) {
closedir(Dir);
Dir = NULL;
} // endif dir
#endif // !_WIN32
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(_WIN32)
/***********************************************************************/
/* 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 // _WIN32
/***********************************************************************/
/* 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(1))
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(_WIN32)
case 0: Value->SetValue_psz(Tdbp->Drive); break;
#endif // _WIN32
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(_WIN32)
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 // !_WIN32
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 // !_WIN32
default:
snprintf(g->Message, sizeof(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(_WIN32)
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);
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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);
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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 // !_WIN32
int k;
DIR *dir = opendir(Direc);
if (!dir) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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 // !_WIN32
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(_WIN32)
Sub->H = INVALID_HANDLE_VALUE;
Sub->Len = strlen(Direc);
#else // !_WIN32
Sub->D = NULL;
Sub->Len = 0;
#endif // !_WIN32
} // 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(_WIN32)
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 // !_WIN32
rc = RC_NF;
again:
if (!Sub->D)
// Start searching files in the target directory.
if (!(Sub->D = opendir(Direc))) {
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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 // !_WIN32
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);
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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(1))
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
snprintf(g->Message, sizeof(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);
snprintf(g->Message, sizeof(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) {
snprintf(g->Message, sizeof(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(1))
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