summaryrefslogtreecommitdiff
path: root/storage/connect/tabdos.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/tabdos.cpp')
-rw-r--r--storage/connect/tabdos.cpp2456
1 files changed, 1228 insertions, 1228 deletions
diff --git a/storage/connect/tabdos.cpp b/storage/connect/tabdos.cpp
index 0f9b280d003..5218a322ea6 100644
--- a/storage/connect/tabdos.cpp
+++ b/storage/connect/tabdos.cpp
@@ -1,1228 +1,1228 @@
-/************* TabDos C++ Program Source Code File (.CPP) **************/
-/* PROGRAM NAME: TABDOS */
-/* ------------- */
-/* Version 4.8 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the DOS tables classes. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <io.h>
-#include <sys\timeb.h> // For testing only
-#include <fcntl.h>
-#include <errno.h>
-#if defined(__BORLANDC__)
-#define __MFC_COMPAT__ // To define min/max as macro
-#endif // __BORLANDC__
-//#include <windows.h>
-#else // !WIN32
-#if defined(UNIX)
-#include <errno.h>
-#include <unistd.h>
-#else // !UNIX
-#include <io.h>
-#endif // !UNIX
-#include <fcntl.h>
-#endif // !WIN32
-
-/***********************************************************************/
-/* Include application header files: */
-/* global.h is header containing all global declarations. */
-/* plgdbsem.h is header containing the DB application declarations. */
-/* filamtxt.h is header containing the file AM classes declarations. */
-/***********************************************************************/
-#include "global.h"
-#include "osutil.h"
-#include "plgdbsem.h"
-#include "catalog.h"
-//#include "reldef.h"
-#include "xindex.h"
-#include "filamap.h"
-#include "filamfix.h"
-#include "filamdbf.h"
-#if defined(ZIP_SUPPORT)
-#include "filamzip.h"
-#endif // ZIP_SUPPORT
-#include "tabdos.h"
-#include "tabfix.h"
-#include "tabmul.h"
-
-#define PLGINI "plugdb.ini" // Configuration settings file
-
-#if defined(UNIX)
-#define _fileno fileno
-#define _O_RDONLY O_RDONLY
-#endif
-
-/***********************************************************************/
-/* DB static variables. */
-/***********************************************************************/
-int num_read, num_there, num_eq[2]; // Statistics
-extern "C" char plgini[_MAX_PATH];
-extern "C" int trace;
-
-/***********************************************************************/
-/* Min and Max blocks contains zero ended fields (blank = false). */
-/* No conversion of block values (check = true). */
-/***********************************************************************/
-PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len = 0, int prec = 0,
- bool check = true, bool blank = false);
-
-/* --------------------------- Class DOSDEF -------------------------- */
-
-/***********************************************************************/
-/* Constructor. */
-/***********************************************************************/
-DOSDEF::DOSDEF(void)
- {
- Pseudo = 3;
- Fn = NULL;
- Ofn = NULL;
- To_Indx = NULL;
- Recfm = RECFM_VAR;
- Mapped = false;
- Padded = false;
- Huge = false;
- Accept = false;
- Eof = false;
- To_Pos = NULL;
- Compressed = 0;
- Lrecl = 0;
- AvgLen = 0;
- Block = 0;
- Last = 0;
- Blksize = 0;
- Maxerr = 0;
- ReadMode = 0;
- Ending = 0;
-//Mtime = 0;
- } // end of DOSDEF constructor
-
-/***********************************************************************/
-/* DeleteTableFile: Delete DOS/UNIX table files using platform API. */
-/* If the table file is protected (declared as read/only) we still */
-/* erase the the eventual optimize and index files but return true. */
-/***********************************************************************/
-bool DOSDEF::DeleteTableFile(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- bool rc = false;
-
- // Now delete the table file itself if not protected
- if (!IsReadOnly()) {
- rc = Erase(filename);
- } else
- rc =true;
-
- return rc; // Return true if error
- } // end of DeleteTableFile
-
-/***********************************************************************/
-/* Erase: This was made a separate routine because a strange thing */
-/* happened when DeleteTablefile was defined for the VCTDEF class: */
-/* when called from Catalog, the DOSDEF routine was still called even */
-/* when the class was VCTDEF. It also minimizes the specific code. */
-/***********************************************************************/
-bool DOSDEF::Erase(char *filename)
- {
- bool rc;
-
- PlugSetPath(filename, Fn, GetPath());
-#if defined(WIN32)
- rc = !DeleteFile(filename);
-#else // UNIX
- rc = remove(filename);
-#endif // UNIX
-
- return rc; // Return true if error
- } // end of Erase
-
-/***********************************************************************/
-/* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */
-/***********************************************************************/
-bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
- {
- char *ftype;
- char filename[_MAX_PATH];
- bool sep, rc = false;
-
- if (!pxdf)
- return false; // No index
-
- sep = Cat->GetSepIndex(); // If true indexes are in separate files
-
- if (!sep && To_Indx) {
- strcpy(g->Message, MSG(NO_RECOV_SPACE));
- return true;
- } // endif sep
-
- switch (Recfm) {
- case RECFM_VAR: ftype = ".dnx"; break;
- case RECFM_FIX: ftype = ".fnx"; break;
- case RECFM_BIN: ftype = ".bnx"; break;
- case RECFM_VCT: ftype = ".vnx"; break;
- case RECFM_DBF: ftype = ".dbx"; break;
- default:
- sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm);
- return true;
- } // endswitch Ftype
-
- /*********************************************************************/
- /* Check for existence of an index file. */
- /*********************************************************************/
- if (sep) {
- // Indexes are save in separate files
-#if !defined(UNIX)
- char drive[_MAX_DRIVE];
-#else
- char *drive = NULL;
-#endif
- char direc[_MAX_DIR];
- char fname[_MAX_FNAME];
-
- for (; pxdf; pxdf = pxdf->GetNext()) {
- _splitpath(Ofn, drive, direc, fname, NULL);
- strcat(strcat(fname, "_"), pxdf->GetName());
- _makepath(filename, drive, direc, fname, ftype);
- PlugSetPath(filename, filename, GetPath());
-#if defined(WIN32)
- rc |= !DeleteFile(filename);
-#else // UNIX
- rc |= remove(filename);
-#endif // UNIX
- } // endfor pxdf
-
- } else { // !sep, no more indexes or name is NULL
- // Drop all indexes, delete the common file
- PlugSetPath(filename, Ofn, GetPath());
- strcat(PlugRemoveType(filename, filename), ftype);
-#if defined(WIN32)
- rc = !DeleteFile(filename);
-#else // UNIX
- rc = remove(filename);
-#endif // UNIX
- } // endif sep
-
- if (rc)
- sprintf(g->Message, MSG(DEL_FILE_ERR), filename);
-
- return rc; // Return true if error
- } // end of DeleteIndexFile
-
-/***********************************************************************/
-/* DefineAM: define specific AM block values from XDB file. */
-/***********************************************************************/
-bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
- {
- char buf[8];
- bool map = (am && (*am == 'M' || *am == 'm'));
- LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F"
- : (am && (*am == 'B' || *am == 'b')) ? "B"
- : (am && !stricmp(am, "DBF")) ? "D" : "V";
-
- Desc = Fn = Cat->GetStringCatInfo(g, Name, "Filename", "");
- Ofn = Cat->GetStringCatInfo(g, Name, "Optname", Fn);
- Cat->GetCharCatInfo(Name, "Recfm", (PSZ)dfm, buf, sizeof(buf));
- Recfm = (toupper(*buf) == 'F') ? RECFM_FIX :
- (toupper(*buf) == 'B') ? RECFM_BIN :
- (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR;
- Lrecl = Cat->GetIntCatInfo(Name, "Lrecl", 0);
-
- if (Recfm != RECFM_DBF)
- Compressed = Cat->GetIntCatInfo(Name, "Compressed", 0);
-
- Mapped = Cat->GetBoolCatInfo(Name, "Mapped", map);
- Block = Cat->GetIntCatInfo(Name, "Blocks", 0);
- Last = Cat->GetIntCatInfo(Name, "Last", 0);
- Ending = Cat->GetIntCatInfo(Name, "Ending", CRLF);
-
- if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) {
- int defhuge = (Cat->GetDefHuge()) ? 1 : 0;
-
- Huge = (Cat->GetIntCatInfo(Name, "Huge", defhuge) != 0);
- Padded = (Cat->GetIntCatInfo(Name, "Padded", 0) != 0);
- Blksize = Cat->GetIntCatInfo(Name, "Blksize", 0);
- Eof = (Cat->GetIntCatInfo(Name, "EOF", 0) != 0);
- } else if (Recfm == RECFM_DBF) {
- Maxerr = Cat->GetIntCatInfo(Name, "Maxerr", 0);
- Accept = (Cat->GetIntCatInfo(Name, "Accept", 0) != 0);
- ReadMode = Cat->GetIntCatInfo(Name, "Readmode", 0);
- } else // (Recfm == RECFM_VAR)
- AvgLen = Cat->GetIntCatInfo(Name, "Avglen", 0);
-
- // Ignore wrong Index definitions for catalog commands
- return (Cat->GetIndexInfo(g, this) /*&& !Cat->GetCatFnc()*/);
- } // end of DefineAM
-
-/***********************************************************************/
-/* InvalidateIndex: mark all indexes as invalid. */
-/***********************************************************************/
-bool DOSDEF::InvalidateIndex(PGLOBAL g)
- {
- if (To_Indx)
- for (PIXDEF xp = To_Indx; xp; xp = xp->Next)
- xp->Invalid = true;
-
- return false;
- } // end of InvalidateIndex
-
-/***********************************************************************/
-/* GetTable: makes a new Table Description Block. */
-/***********************************************************************/
-PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
- {
- // Mapping not used for insert
- USETEMP tmp = PlgGetUser(g)->UseTemp;
- bool map = Mapped && mode != MODE_INSERT &&
- !(tmp != TMP_NO && Recfm == RECFM_VAR
- && mode == MODE_UPDATE) &&
- !(tmp == TMP_FORCE &&
- (mode == MODE_UPDATE || mode == MODE_DELETE));
- PTXF txfp;
- PTDBASE tdbp;
-
- /*********************************************************************/
- /* Allocate table and file processing class of the proper type. */
- /* Column blocks will be allocated only when needed. */
- /*********************************************************************/
- if (Recfm == RECFM_DBF) {
- if (map)
- txfp = new(g) DBMFAM(this);
- else
- txfp = new(g) DBFFAM(this);
-
- tdbp = new(g) TDBFIX(this, txfp);
- } else if (Recfm != RECFM_VAR && Compressed < 2) {
- if (Huge)
- txfp = new(g) BGXFAM(this);
- else if (map)
- txfp = new(g) MPXFAM(this);
-#if defined(ZIP_SUPPORT)
- else if (Compressed)
- txfp = new(g) ZIXFAM(this);
-#endif // ZIP_SUPPORT
- else
- txfp = new(g) FIXFAM(this);
-
- tdbp = new(g) TDBFIX(this, txfp);
- } else {
-#if defined(ZIP_SUPPORT)
- if (Compressed) {
- if (Compressed == 1)
- txfp = new(g) ZIPFAM(this);
- else {
- strcpy(g->Message, "Compress 2 not supported yet");
-// txfp = new(g) ZLBFAM(defp);
- return NULL;
- } // endelse
-
- } else
-#endif // ZIP_SUPPORT
- if (map)
- txfp = new(g) MAPFAM(this);
- else
- txfp = new(g) DOSFAM(this);
-
- // Txfp must be set even for not multiple tables because
- // it is needed when calling Cardinality in GetBlockValues.
- tdbp = new(g) TDBDOS(this, txfp);
- } // endif Recfm
-
- if (Multiple)
- tdbp = new(g) TDBMUL(tdbp);
-
- return tdbp;
- } // end of GetTable
-
-/* ------------------------ Class TDBDOS ----------------------------- */
-
-/***********************************************************************/
-/* Implementation of the TDBDOS class. This is the common class that */
-/* contain all that is common between the TDBDOS and TDBMAP classes. */
-/***********************************************************************/
-TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
- {
- if ((Txfp = txfp))
- Txfp->SetTdbp(this);
-
- Lrecl = tdp->Lrecl;
- AvgLen = tdp->AvgLen;
- Ftype = tdp->Recfm;
- To_Line = NULL;
- Cardinal = -1;
- } // end of TDBDOS standard constructor
-
-TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
- {
- Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp;
- Lrecl = tdbp->Lrecl;
- AvgLen = tdbp->AvgLen;
- Ftype = tdbp->Ftype;
- To_Line = tdbp->To_Line;
- Cardinal = tdbp->Cardinal;
- } // end of TDBDOS copy constructor
-
-// Method
-PTDB TDBDOS::CopyOne(PTABS t)
- {
- PTDB tp;
- PDOSCOL cp1, cp2;
- PGLOBAL g = t->G;
-
- tp = new(g) TDBDOS(g, this);
-
- for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
- cp2 = new(g) DOSCOL(cp1, tp); // Make a copy
- NewPointer(t, cp1, cp2);
- } // endfor cp1
-
- return tp;
- } // end of CopyOne
-
-/***********************************************************************/
-/* Allocate DOS column description block. */
-/***********************************************************************/
-PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
- {
- return new(g) DOSCOL(g, cdp, this, cprec, n);
- } // end of MakeCol
-
-/***********************************************************************/
-/* Print debug information. */
-/***********************************************************************/
-void TDBDOS::PrintAM(FILE *f, char *m)
- {
- fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
-
- if (Txfp->To_File)
- fprintf(f, "%s File: %s\n", m, Txfp->To_File);
-
- } // end of PrintAM
-
-/***********************************************************************/
-/* Remake the indexes after the table was modified. */
-/***********************************************************************/
-int TDBDOS::ResetTableOpt(PGLOBAL g, bool dox)
- {
- int rc = RC_OK;
-
- MaxSize = -1; // Size must be recalculated
- Cardinal = -1; // as well as Cardinality
-
- if (dox) {
- // Remake eventual indexes
- Columns = NULL; // Not used anymore
- Txfp->Reset(); // New start
- Use = USE_READY; // So the table can be reopened
- Mode = MODE_READ; // New mode
-
- if (!(PlgGetUser(g)->Check & CHK_OPT)) {
- // After the table was modified the indexes
- // are invalid and we should mark them as such...
- rc = ((PDOSDEF)To_Def)->InvalidateIndex(g);
- } else
- // ... or we should remake them.
- rc = MakeIndex(g, NULL, false);
-
- } // endif dox
-
- return rc;
- } // end of ResetTableOpt
-
-/***********************************************************************/
-/* Check whether we have to create/update permanent indexes. */
-/***********************************************************************/
-int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
- {
- int k, n;
- bool fixed, b = (pxdf != NULL);
- PCOL *keycols, colp;
- PIXDEF xdp, sxp = NULL;
- PKPDEF kdp;
- PDOSDEF dfp;
-//PCOLDEF cdp;
- PXINDEX x;
- PXLOAD pxp;
- PCATLG cat = PlgGetCatalog(g);
-
- Mode = MODE_READ;
- Use = USE_READY;
- dfp = (PDOSDEF)To_Def;
- fixed = Cardinality(g) >= 0;
-
- // Are we are called from CreateTable or CreateIndex?
- if (pxdf) {
- if (!add && dfp->GetIndx()) {
- strcpy(g->Message, MSG(INDX_EXIST_YET));
- return RC_FX;
- } // endif To_Indx
-
- if (add && dfp->GetIndx()) {
- for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext())
- if (!stricmp(sxp->GetName(), pxdf->GetName())) {
- sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name);
- return RC_FX;
- } else if (!sxp->GetNext())
- break;
-
- sxp->SetNext(pxdf);
-// first = false;
- } else
- dfp->SetIndx(pxdf);
-
-// pxdf->SetDef(dfp);
- } else if (!(pxdf = dfp->GetIndx()))
- return RC_INFO; // No index to make
-
- // Allocate all columns that will be used by indexes.
- // This must be done before opening the table so specific
- // column initialization can be done ( in particular by TDBVCT)
- for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext())
- for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
- if (!(colp = ColDB(g, kdp->GetName(), 0))) {
- sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name);
- goto err;
- } // endif colp
-
- colp->InitValue(g);
- n = max(n, xdp->GetNparts());
- } // endfor kdp
-
- keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL));
-
- /*********************************************************************/
- /* Construct and save the defined indexes. */
- /*********************************************************************/
- for (xdp = pxdf; xdp; xdp = xdp->GetNext())
- if (!OpenDB(g)) {
- if (xdp->IsAuto() && fixed)
- // Auto increment key and fixed file: use an XXROW index
- continue; // XXROW index doesn't need to be made
-
- n = 0;
-
- if (sxp)
- xdp->SetID(sxp->GetID() + 1);
-
- for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext())
- keycols[n++] = ColDB(g, kdp->GetName(), 0);
-
- k = xdp->GetNparts();
-
- // Make the index and save it
- if (dfp->Huge)
- pxp = new(g) XHUGE;
- else
- pxp = new(g) XFILE;
-
- if (k == 1) // Simple index
- x = new(g) XINDXS(this, xdp, pxp, keycols);
- else // Multi-Column index
- x = new(g) XINDEX(this, xdp, pxp, keycols);
-
- if (!x->Make(g, sxp)) {
- // Retreive define values from the index
- xdp->SetMaxSame(x->GetMaxSame());
- xdp->SetOffset(x->GetDefoff());
- xdp->SetOffhigh(x->GetDefhigh());
- xdp->SetSize(x->GetSize());
-
- // store KXYCOL Mxs in KPARTDEF Mxsame
- xdp->SetMxsame(x);
-
-#if defined(TRACE)
- printf("Make done...\n");
-#endif // TRACE
-
- if (x->GetSize() > 0)
- sxp = xdp;
-
- xdp->SetInvalid(false);
- } else
- goto err;
-
- } else
- break; // Physical table does not exist yet or anymore
-
- if (Use == USE_OPEN)
- CloseDB(g);
-
- return RC_OK;
-
-err:
- if (sxp)
- sxp->SetNext(NULL);
- else
- dfp->SetIndx(NULL);
-
- return RC_FX;
- } // end of MakeIndex
-
-/***********************************************************************/
-/* DOS GetProgMax: get the max value for progress information. */
-/***********************************************************************/
-int TDBDOS::GetProgMax(PGLOBAL g)
- {
- return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g);
- } // end of GetProgMax
-
-/***********************************************************************/
-/* DOS GetProgCur: get the current value for progress information. */
-/***********************************************************************/
-int TDBDOS::GetProgCur(void)
- {
- return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos();
- } // end of GetProgCur
-
-/***********************************************************************/
-/* RowNumber: return the ordinal number of the current row. */
-/***********************************************************************/
-int TDBDOS::RowNumber(PGLOBAL g, bool b)
- {
- if (To_Kindex) {
- /*******************************************************************/
- /* Don't know how to retrieve RowID from file address. */
- /*******************************************************************/
- sprintf(g->Message, MSG(NO_ROWID_FOR_AM),
- GetAmName(g, Txfp->GetAmType()));
- return 0;
- } else
- return Txfp->GetRowID();
-
- } // end of RowNumber
-
-/***********************************************************************/
-/* DOS Cardinality: returns table cardinality in number of rows. */
-/* This function can be called with a null argument to test the */
-/* availability of Cardinality implementation (1 yes, 0 no). */
-/***********************************************************************/
-int TDBDOS::Cardinality(PGLOBAL g)
- {
- if (!g)
- return Txfp->Cardinality(g);
-
- if (Cardinal < 0)
- Cardinal = Txfp->Cardinality(g);
-
- return Cardinal;
- } // end of Cardinality
-
-/***********************************************************************/
-/* DOS GetMaxSize: returns file size estimate in number of lines. */
-/* This function covers variable record length files. */
-/***********************************************************************/
-int TDBDOS::GetMaxSize(PGLOBAL g)
- {
- if (MaxSize >= 0)
- return MaxSize;
-
- if (!Cardinality(NULL)) {
- int len = GetFileLength(g);
-
- if (len >= 0) {
- if (trace)
- htrc("Estimating lines len=%d ending=%d\n",
- len, ((PDOSDEF)To_Def)->Ending);
-
- /*****************************************************************/
- /* Estimate the number of lines in the table (if not known) by */
- /* dividing the file length by the minimum line length assuming */
- /* only the last column can be of variable length. This will be */
- /* a ceiling estimate (as last column is never totally absent). */
- /*****************************************************************/
- int rec = ((PDOSDEF)To_Def)->Ending; // +2: CRLF +1: LF
-
- if (AvgLen <= 0) // No given average estimate
- rec += EstimatedLength(g);
- else // A lower estimate was given for the average record length
- rec += (int)AvgLen;
-
- if (trace)
- htrc(" Filen=%d min_rec=%d\n", len, rec);
-
- MaxSize = (len + rec - 1) / rec;
-
- if (trace)
- htrc(" Estimated max_K=%d\n", MaxSize);
-
- } // endif len
-
- } else
- MaxSize = Cardinality(g);
-
- return MaxSize;
- } // end of GetMaxSize
-
-/***********************************************************************/
-/* DOS EstimatedLength. Returns an estimated minimum line length. */
-/***********************************************************************/
-int TDBDOS::EstimatedLength(PGLOBAL g)
- {
- int dep = 0;
- PCOLDEF cdp = To_Def->GetCols();
-
- if (!cdp->GetNext()) {
- // One column table, we are going to return a ridiculous
- // result if we set dep to 1
- dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
- } else for (; cdp; cdp = cdp->GetNext())
- dep = max(dep, cdp->GetOffset());
-
- return (int)dep;
- } // end of Estimated Length
-
-/***********************************************************************/
-/* DOS tables favor the use temporary files for Update. */
-/***********************************************************************/
-bool TDBDOS::IsUsingTemp(PGLOBAL g)
- {
- USETEMP usetemp = PlgGetUser(g)->UseTemp;
-
- return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
- (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
- } // end of IsUsingTemp
-
-/***********************************************************************/
-/* DOS Access Method opening routine. */
-/* New method now that this routine is called recursively (last table */
-/* first in reverse order): index blocks are immediately linked to */
-/* join block of next table if it exists or else are discarted. */
-/***********************************************************************/
-bool TDBDOS::OpenDB(PGLOBAL g)
- {
- if (trace)
- htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
- this, Tdb_No, Use, Mode);
-
- if (Use == USE_OPEN) {
- /*******************************************************************/
- /* Table already open, just replace it at its beginning. */
- /*******************************************************************/
- Txfp->Rewind(); // see comment in Work.log
-
- if (SkipHeader(g))
- return true;
-
- return false;
- } // endif use
-
- if (Txfp->Blocked && (Mode == MODE_DELETE ||
- (Mode == MODE_UPDATE && PlgGetUser(g)->UseTemp != TMP_NO))) {
- /*******************************************************************/
- /* Delete is not currently handled in block mode neither Update */
- /* when using a temporary file. */
- /*******************************************************************/
- if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE)
- Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
-#if defined(ZIP_SUPPORT)
- else if (Txfp->GetAmType() == TYPE_AM_ZIP)
- Txfp = new(g) ZIPFAM((PDOSDEF)To_Def);
-#endif // ZIP_SUPPORT
- else if (Txfp->GetAmType() != TYPE_AM_DOS)
- Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
-
- Txfp->SetTdbp(this);
- } // endif Mode
-
- /*********************************************************************/
- /* Open according to logical input/output mode required. */
- /* Use conventionnal input/output functions. */
- /* Treat files as binary in Delete mode (for line moving) */
- /*********************************************************************/
- if (Txfp->OpenTableFile(g))
- return true;
-
- Use = USE_OPEN; // Do it now in case we are recursively called
-
- /*********************************************************************/
- /* Allocate the line buffer plus a null character. */
- /*********************************************************************/
- To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1);
-
- if (Mode == MODE_INSERT) {
- // Spaces between fields must be filled with blanks
- memset(To_Line, ' ', Lrecl);
- To_Line[Lrecl] = '\0';
- } else
- memset(To_Line, 0, Lrecl + 1);
-
- if (trace)
- htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
-
- if (SkipHeader(g)) // When called from CSV/FMT files
- return true;
-
- /*********************************************************************/
- /* Reset statistics values. */
- /*********************************************************************/
- num_read = num_there = num_eq[0] = num_eq[1] = 0;
- return false;
- } // end of OpenDB
-
-/***********************************************************************/
-/* ReadDB: Data Base read routine for DOS access method. */
-/***********************************************************************/
-int TDBDOS::ReadDB(PGLOBAL g)
- {
- if (trace > 1)
- htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n",
- GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line);
-
- if (To_Kindex) {
- /*******************************************************************/
- /* Reading is by an index table. */
- /*******************************************************************/
- int recpos = To_Kindex->Fetch(g);
-
- switch (recpos) {
- case -1: // End of file reached
- return RC_EF;
- case -2: // No match for join
- return RC_NF;
- case -3: // Same record as last non null one
- num_there++;
- return RC_OK;
- default:
- /***************************************************************/
- /* Set the file position according to record to read. */
- /***************************************************************/
- if (SetRecpos(g, recpos))
- return RC_FX;
-
- if (trace > 1)
- htrc("File position is now %d\n", GetRecpos());
-
- if (Mode == MODE_READ)
- /*************************************************************/
- /* Defer physical reading until one column setting needs it */
- /* as it can be a big saving on joins where no other column */
- /* than the keys are used, so reading is unnecessary. */
- /*************************************************************/
- if (Txfp->DeferReading())
- return RC_OK;
-
- } // endswitch recpos
-
- } // endif To_Kindex
-
- if (trace > 1)
- htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line);
-
- /*********************************************************************/
- /* Now start the reading process. */
- /*********************************************************************/
- return ReadBuffer(g);
- } // end of ReadDB
-
-/***********************************************************************/
-/* WriteDB: Data Base write routine for DOS access method. */
-/***********************************************************************/
-int TDBDOS::WriteDB(PGLOBAL g)
- {
- if (trace > 1)
- htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);
-
- if (!Ftype && (Mode == MODE_INSERT || Txfp->GetUseTemp())) {
- char *p;
-
- /*******************************************************************/
- /* Suppress trailing blanks. */
- /* Also suppress eventual null from last line. */
- /*******************************************************************/
- for (p = To_Line + Lrecl -1; p >= To_Line; p--)
- if (*p && *p != ' ')
- break;
-
- *(++p) = '\0';
- } // endif Mode
-
- if (trace > 1)
- htrc("Write: line is='%s'\n", To_Line);
-
- // Now start the writing process
- return Txfp->WriteBuffer(g);
- } // end of WriteDB
-
-/***********************************************************************/
-/* Data Base delete line routine for DOS (and FIX) access method. */
-/* RC_FX means delete all. Nothing to do here (was done at open). */
-/***********************************************************************/
-int TDBDOS::DeleteDB(PGLOBAL g, int irc)
- {
- return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc);
- } // end of DeleteDB
-
-/***********************************************************************/
-/* Data Base close routine for DOS access method. */
-/***********************************************************************/
-void TDBDOS::CloseDB(PGLOBAL g)
- {
- if (To_Kindex) {
- To_Kindex->Close();
- To_Kindex = NULL;
- } // endif
-
- Txfp->CloseTableFile(g);
- } // end of CloseDB
-
-// ------------------------ DOSCOL functions ----------------------------
-
-/***********************************************************************/
-/* DOSCOL public constructor (also called by MAPCOL). */
-/***********************************************************************/
-DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am)
- : COLBLK(cdp, tp, i)
- {
- char *p;
- int prec = Format.Prec;
- PTXF txfp = ((PTDBDOS)tp)->Txfp;
-
- assert(cdp);
-
- if (cp) {
- Next = cp->GetNext();
- cp->SetNext(this);
- } else {
- Next = tp->GetColumns();
- tp->SetColumns(this);
- } // endif cprec
-
- // Set additional Dos access method information for column.
- Deplac = cdp->GetOffset();
- Long = cdp->GetLong();
- To_Val = NULL;
-
- OldVal = NULL; // Currently used only in MinMax
- Ldz = false;
- Nod = false;
- Dcm = -1;
- p = cdp->GetFmt();
-
- if (p && IsTypeNum(Buf_Type)) {
- // Formatted numeric value
- for (; p && *p && isalpha(*p); p++)
- switch (toupper(*p)) {
- case 'Z': // Have leading zeros
- Ldz = true;
- break;
- case 'N': // Have no decimal point
- Nod = true;
- break;
- } // endswitch p
-
- // Set number of decimal digits
- Dcm = (*p) ? atoi(p) : GetPrecision();
- } // endif fmt
-
- if (trace)
- htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
-
- } // end of DOSCOL constructor
-
-/***********************************************************************/
-/* DOSCOL constructor used for copying columns. */
-/* tdbp is the pointer to the new table descriptor. */
-/***********************************************************************/
-DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
- {
- Deplac = col1->Deplac;
- Long = col1->Long;
- To_Val = col1->To_Val;
- Ldz = col1->Ldz;
- Nod = col1->Nod;
- Dcm = col1->Dcm;
- OldVal = col1->OldVal;
- Buf = col1->Buf;
- } // end of DOSCOL copy constructor
-
-/***********************************************************************/
-/* VarSize: This function tells UpdateDB whether or not the block */
-/* optimization file must be redone if this column is updated, even */
-/* it is not sorted or clustered. This applies to the last column of */
-/* a variable length table that is blocked, because if it is updated */
-/* using a temporary file, the block size may be modified. */
-/***********************************************************************/
-bool DOSCOL::VarSize(void)
- {
- PTDBDOS tdbp = (PTDBDOS)To_Tdb;
- PTXF txfp = tdbp->Txfp;
-
- if (Cdp && !Cdp->GetNext() // Must be the last column
- && tdbp->Ftype == RECFM_VAR // of a DOS variable length
- && txfp->Blocked // blocked table
- && txfp->GetUseTemp()) // using a temporary file.
- return true;
- else
- return false;
-
- } // end VarSize
-
-/***********************************************************************/
-/* SetBuffer: prepare a column block for write operation. */
-/***********************************************************************/
-bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
- {
- if (!(To_Val = value)) {
- sprintf(g->Message, MSG(VALUE_ERROR), Name);
- return true;
- } else if (Buf_Type == value->GetType()) {
- // Values are of the (good) column type
- if (Buf_Type == TYPE_DATE) {
- // If any of the date values is formatted
- // output format must be set for the receiving table
- if (GetDomain() || ((DTVAL *)value)->IsFormatted())
- goto newval; // This will make a new value;
-
- } else if (Buf_Type == TYPE_FLOAT)
- // Float values must be written with the correct (column) precision
- // Note: maybe this should be forced by ShowValue instead of this ?
- ((DFVAL *)value)->SetPrec(GetPrecision());
-
- Value = value; // Directly access the external value
- } else {
- // Values are not of the (good) column type
- if (check) {
- sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
- GetTypeName(Buf_Type), GetTypeName(value->GetType()));
- return true;
- } // endif check
-
- newval:
- if (InitValue(g)) // Allocate the matching value block
- return true;
-
- } // endif's Value, Buf_Type
-
- // Allocate the buffer used in WriteColumn for numeric columns
- if (IsTypeNum(Buf_Type))
- Buf = (char*)PlugSubAlloc(g, NULL, max(32, Long + Dcm + 1));
-
- // Because Colblk's have been made from a copy of the original TDB in
- // case of Update, we must reset them to point to the original one.
- if (To_Tdb->GetOrig())
- To_Tdb = (PTDB)To_Tdb->GetOrig();
-
- // Set the Column
- Status = (ok) ? BUF_EMPTY : BUF_NO;
- return false;
- } // end of SetBuffer
-
-/***********************************************************************/
-/* ReadColumn: what this routine does is to access the last line */
-/* read from the corresponding table, extract from it the field */
-/* corresponding to this column and convert it to buffer type. */
-/***********************************************************************/
-void DOSCOL::ReadColumn(PGLOBAL g)
- {
- char *p;
- int i, rc;
- int field;
- PTDBDOS tdbp = (PTDBDOS)To_Tdb;
-
- if (trace > 1)
- htrc(
- "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
- Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);
-
- /*********************************************************************/
- /* If physical reading of the line was deferred, do it now. */
- /*********************************************************************/
- if (!tdbp->IsRead())
- if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
- if (rc == RC_EF)
- sprintf(g->Message, MSG(INV_DEF_READ), rc);
-
- longjmp(g->jumper[g->jump_level], 11);
- } // endif
-
- p = tdbp->To_Line + Deplac;
- field = Long;
-
- switch (tdbp->Ftype) {
- case RECFM_VAR:
- /*****************************************************************/
- /* For a variable length file, check if the field exists. */
- /*****************************************************************/
- if (strlen(tdbp->To_Line) < (unsigned)Deplac)
- field = 0;
-
- case RECFM_FIX: // Fixed length text file
- case RECFM_DBF: // Fixed length DBase file
- if (Nod) switch (Buf_Type) {
- case TYPE_INT:
- case TYPE_SHORT:
- Value->SetValue_char(p, field - Dcm);
- break;
- case TYPE_FLOAT:
- Value->SetValue_char(p, field);
-
- for (i = 0; i < Dcm; i++)
- ((DFVAL*)Value)->Divide(10.0);
-
- break;
- default:
- Value->SetValue_char(p, field);
- break;
- } // endswitch Buf_Type
-
- else
- Value->SetValue_char(p, field);
-
- break;
- default:
- sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype);
- longjmp(g->jumper[g->jump_level], 34);
- } // endswitch Ftype
-
- } // end of ReadColumn
-
-/***********************************************************************/
-/* WriteColumn: what this routine does is to access the last line */
-/* read from the corresponding table, and rewrite the field */
-/* corresponding to this column from the column buffer and type. */
-/***********************************************************************/
-void DOSCOL::WriteColumn(PGLOBAL g)
- {
- char *p, *p2, fmt[32];
- int i, k, len, field;
- PTDBDOS tdbp = (PTDBDOS)To_Tdb;
-
- if (trace > 1)
- htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
- Name, tdbp->GetTdb_No(), ColUse, Status);
-
- p = tdbp->To_Line + Deplac;
-
- if (trace > 1)
- htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long);
-
- field = Long;
-
- if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) {
- len = (signed)strlen(tdbp->To_Line);
-
- if (tdbp->IsUsingTemp(g))
- // Because of eventual missing field(s) the buffer must be reset
- memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len);
- else
- // The size actually available must be recalculated
- field = min(len - Deplac, Long);
-
- } // endif Ftype
-
- if (trace > 1)
- htrc("Long=%d field=%d coltype=%d colval=%p\n",
- Long, field, Buf_Type, Value);
-
- /*********************************************************************/
- /* Get the string representation of Value according to column type. */
- /*********************************************************************/
- if (Value != To_Val)
- Value->SetValue_pval(To_Val, false); // Convert the updated value
-
- /*********************************************************************/
- /* This test is only useful for compressed(2) tables. */
- /*********************************************************************/
- if (tdbp->Ftype != RECFM_BIN) {
- if (Ldz || Nod || Dcm >= 0) {
- switch (Buf_Type) {
- case TYPE_SHORT:
- strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd");
- i = 0;
-
- if (Nod)
- for (; i < Dcm; i++)
- strcat(fmt, "0");
-
- len = sprintf(Buf, fmt, field - i, Value->GetShortValue());
- break;
- case TYPE_INT:
- strcpy(fmt, (Ldz) ? "%0*ld" : "%*.ld");
- i = 0;
-
- if (Nod)
- for (; i < Dcm; i++)
- strcat(fmt, "0");
-
- len = sprintf(Buf, fmt, field - i, Value->GetIntValue());
- break;
- case TYPE_FLOAT:
- strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf");
- sprintf(Buf, fmt, field + ((Nod && Dcm) ? 1 : 0),
- Dcm, Value->GetFloatValue());
- len = strlen(Buf);
-
- if (Nod && Dcm)
- for (i = k = 0; i < len; i++, k++)
- if (Buf[i] != ' ') {
- if (Buf[i] == '.' || Buf[i] == ',')
- k++;
-
- Buf[i] = Buf[k];
- } // endif Buf(i)
-
- len = strlen(Buf);
- break;
- } // endswitch BufType
-
- p2 = Buf;
- } else // Standard PlugDB format
- p2 = Value->ShowValue(Buf, field);
-
- if (trace)
- htrc("new length(%p)=%d\n", p2, strlen(p2));
-
- if ((len = strlen(p2)) > field) {
- sprintf(g->Message, MSG(VALUE_TOO_LONG), p2, Name, field);
- longjmp(g->jumper[g->jump_level], 31);
- } // endif
-
- if (trace > 1)
- htrc("buffer=%s\n", p2);
-
- /*******************************************************************/
- /* Updating must be done only when not in checking pass. */
- /*******************************************************************/
- if (Status) {
- memset(p, ' ', field);
- memcpy(p, p2, len);
-
- if (trace > 1)
- htrc(" col write: '%.*s'\n", len, p);
-
- } // endif Use
-
- } else // BIN compressed table
- /*******************************************************************/
- /* Check if updating is Ok, meaning col value is not too long. */
- /* Updating to be done only during the second pass (Status=true) */
- /*******************************************************************/
- if (Value->GetBinValue(p, Long, Status)) {
- sprintf(g->Message, MSG(BIN_F_TOO_LONG),
- Name, Value->GetSize(), Long);
- longjmp(g->jumper[g->jump_level], 31);
- } // endif
-
- } // end of WriteColumn
-
-/***********************************************************************/
-/* Make file output of a Dos column descriptor block. */
-/***********************************************************************/
-void DOSCOL::Print(PGLOBAL g, FILE *f, uint n)
- {
- COLBLK::Print(g, f, n);
- } // end of Print
-
-/* ------------------------------------------------------------------- */
-
+/************* TabDos C++ Program Source Code File (.CPP) **************/
+/* PROGRAM NAME: TABDOS */
+/* ------------- */
+/* Version 4.8 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 1998-2012 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the DOS tables classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <io.h>
+#include <sys\timeb.h> // For testing only
+#include <fcntl.h>
+#include <errno.h>
+#if defined(__BORLANDC__)
+#define __MFC_COMPAT__ // To define min/max as macro
+#endif // __BORLANDC__
+//#include <windows.h>
+#else // !WIN32
+#if defined(UNIX)
+#include <errno.h>
+#include <unistd.h>
+#else // !UNIX
+#include <io.h>
+#endif // !UNIX
+#include <fcntl.h>
+#endif // !WIN32
+
+/***********************************************************************/
+/* Include application header files: */
+/* global.h is header containing all global declarations. */
+/* plgdbsem.h is header containing the DB application declarations. */
+/* filamtxt.h is header containing the file AM classes declarations. */
+/***********************************************************************/
+#include "global.h"
+#include "osutil.h"
+#include "plgdbsem.h"
+#include "catalog.h"
+//#include "reldef.h"
+#include "xindex.h"
+#include "filamap.h"
+#include "filamfix.h"
+#include "filamdbf.h"
+#if defined(ZIP_SUPPORT)
+#include "filamzip.h"
+#endif // ZIP_SUPPORT
+#include "tabdos.h"
+#include "tabfix.h"
+#include "tabmul.h"
+
+#define PLGINI "plugdb.ini" // Configuration settings file
+
+#if defined(UNIX)
+#define _fileno fileno
+#define _O_RDONLY O_RDONLY
+#endif
+
+/***********************************************************************/
+/* DB static variables. */
+/***********************************************************************/
+int num_read, num_there, num_eq[2]; // Statistics
+extern "C" char plgini[_MAX_PATH];
+extern "C" int trace;
+
+/***********************************************************************/
+/* Min and Max blocks contains zero ended fields (blank = false). */
+/* No conversion of block values (check = true). */
+/***********************************************************************/
+PVBLK AllocValBlock(PGLOBAL, void *, int, int, int len = 0, int prec = 0,
+ bool check = true, bool blank = false);
+
+/* --------------------------- Class DOSDEF -------------------------- */
+
+/***********************************************************************/
+/* Constructor. */
+/***********************************************************************/
+DOSDEF::DOSDEF(void)
+ {
+ Pseudo = 3;
+ Fn = NULL;
+ Ofn = NULL;
+ To_Indx = NULL;
+ Recfm = RECFM_VAR;
+ Mapped = false;
+ Padded = false;
+ Huge = false;
+ Accept = false;
+ Eof = false;
+ To_Pos = NULL;
+ Compressed = 0;
+ Lrecl = 0;
+ AvgLen = 0;
+ Block = 0;
+ Last = 0;
+ Blksize = 0;
+ Maxerr = 0;
+ ReadMode = 0;
+ Ending = 0;
+//Mtime = 0;
+ } // end of DOSDEF constructor
+
+/***********************************************************************/
+/* DeleteTableFile: Delete DOS/UNIX table files using platform API. */
+/* If the table file is protected (declared as read/only) we still */
+/* erase the the eventual optimize and index files but return true. */
+/***********************************************************************/
+bool DOSDEF::DeleteTableFile(PGLOBAL g)
+ {
+ char filename[_MAX_PATH];
+ bool rc = false;
+
+ // Now delete the table file itself if not protected
+ if (!IsReadOnly()) {
+ rc = Erase(filename);
+ } else
+ rc =true;
+
+ return rc; // Return true if error
+ } // end of DeleteTableFile
+
+/***********************************************************************/
+/* Erase: This was made a separate routine because a strange thing */
+/* happened when DeleteTablefile was defined for the VCTDEF class: */
+/* when called from Catalog, the DOSDEF routine was still called even */
+/* when the class was VCTDEF. It also minimizes the specific code. */
+/***********************************************************************/
+bool DOSDEF::Erase(char *filename)
+ {
+ bool rc;
+
+ PlugSetPath(filename, Fn, GetPath());
+#if defined(WIN32)
+ rc = !DeleteFile(filename);
+#else // UNIX
+ rc = remove(filename);
+#endif // UNIX
+
+ return rc; // Return true if error
+ } // end of Erase
+
+/***********************************************************************/
+/* DeleteIndexFile: Delete DOS/UNIX index file(s) using platform API. */
+/***********************************************************************/
+bool DOSDEF::DeleteIndexFile(PGLOBAL g, PIXDEF pxdf)
+ {
+ char *ftype;
+ char filename[_MAX_PATH];
+ bool sep, rc = false;
+
+ if (!pxdf)
+ return false; // No index
+
+ sep = Cat->GetSepIndex(); // If true indexes are in separate files
+
+ if (!sep && To_Indx) {
+ strcpy(g->Message, MSG(NO_RECOV_SPACE));
+ return true;
+ } // endif sep
+
+ switch (Recfm) {
+ case RECFM_VAR: ftype = ".dnx"; break;
+ case RECFM_FIX: ftype = ".fnx"; break;
+ case RECFM_BIN: ftype = ".bnx"; break;
+ case RECFM_VCT: ftype = ".vnx"; break;
+ case RECFM_DBF: ftype = ".dbx"; break;
+ default:
+ sprintf(g->Message, MSG(BAD_RECFM_VAL), Recfm);
+ return true;
+ } // endswitch Ftype
+
+ /*********************************************************************/
+ /* Check for existence of an index file. */
+ /*********************************************************************/
+ if (sep) {
+ // Indexes are save in separate files
+#if !defined(UNIX)
+ char drive[_MAX_DRIVE];
+#else
+ char *drive = NULL;
+#endif
+ char direc[_MAX_DIR];
+ char fname[_MAX_FNAME];
+
+ for (; pxdf; pxdf = pxdf->GetNext()) {
+ _splitpath(Ofn, drive, direc, fname, NULL);
+ strcat(strcat(fname, "_"), pxdf->GetName());
+ _makepath(filename, drive, direc, fname, ftype);
+ PlugSetPath(filename, filename, GetPath());
+#if defined(WIN32)
+ rc |= !DeleteFile(filename);
+#else // UNIX
+ rc |= remove(filename);
+#endif // UNIX
+ } // endfor pxdf
+
+ } else { // !sep, no more indexes or name is NULL
+ // Drop all indexes, delete the common file
+ PlugSetPath(filename, Ofn, GetPath());
+ strcat(PlugRemoveType(filename, filename), ftype);
+#if defined(WIN32)
+ rc = !DeleteFile(filename);
+#else // UNIX
+ rc = remove(filename);
+#endif // UNIX
+ } // endif sep
+
+ if (rc)
+ sprintf(g->Message, MSG(DEL_FILE_ERR), filename);
+
+ return rc; // Return true if error
+ } // end of DeleteIndexFile
+
+/***********************************************************************/
+/* DefineAM: define specific AM block values from XDB file. */
+/***********************************************************************/
+bool DOSDEF::DefineAM(PGLOBAL g, LPCSTR am, int poff)
+ {
+ char buf[8];
+ bool map = (am && (*am == 'M' || *am == 'm'));
+ LPCSTR dfm = (am && (*am == 'F' || *am == 'f')) ? "F"
+ : (am && (*am == 'B' || *am == 'b')) ? "B"
+ : (am && !stricmp(am, "DBF")) ? "D" : "V";
+
+ Desc = Fn = Cat->GetStringCatInfo(g, Name, "Filename", "");
+ Ofn = Cat->GetStringCatInfo(g, Name, "Optname", Fn);
+ Cat->GetCharCatInfo(Name, "Recfm", (PSZ)dfm, buf, sizeof(buf));
+ Recfm = (toupper(*buf) == 'F') ? RECFM_FIX :
+ (toupper(*buf) == 'B') ? RECFM_BIN :
+ (toupper(*buf) == 'D') ? RECFM_DBF : RECFM_VAR;
+ Lrecl = Cat->GetIntCatInfo(Name, "Lrecl", 0);
+
+ if (Recfm != RECFM_DBF)
+ Compressed = Cat->GetIntCatInfo(Name, "Compressed", 0);
+
+ Mapped = Cat->GetBoolCatInfo(Name, "Mapped", map);
+ Block = Cat->GetIntCatInfo(Name, "Blocks", 0);
+ Last = Cat->GetIntCatInfo(Name, "Last", 0);
+ Ending = Cat->GetIntCatInfo(Name, "Ending", CRLF);
+
+ if (Recfm == RECFM_FIX || Recfm == RECFM_BIN) {
+ int defhuge = (Cat->GetDefHuge()) ? 1 : 0;
+
+ Huge = (Cat->GetIntCatInfo(Name, "Huge", defhuge) != 0);
+ Padded = (Cat->GetIntCatInfo(Name, "Padded", 0) != 0);
+ Blksize = Cat->GetIntCatInfo(Name, "Blksize", 0);
+ Eof = (Cat->GetIntCatInfo(Name, "EOF", 0) != 0);
+ } else if (Recfm == RECFM_DBF) {
+ Maxerr = Cat->GetIntCatInfo(Name, "Maxerr", 0);
+ Accept = (Cat->GetIntCatInfo(Name, "Accept", 0) != 0);
+ ReadMode = Cat->GetIntCatInfo(Name, "Readmode", 0);
+ } else // (Recfm == RECFM_VAR)
+ AvgLen = Cat->GetIntCatInfo(Name, "Avglen", 0);
+
+ // Ignore wrong Index definitions for catalog commands
+ return (Cat->GetIndexInfo(g, this) /*&& !Cat->GetCatFnc()*/);
+ } // end of DefineAM
+
+/***********************************************************************/
+/* InvalidateIndex: mark all indexes as invalid. */
+/***********************************************************************/
+bool DOSDEF::InvalidateIndex(PGLOBAL g)
+ {
+ if (To_Indx)
+ for (PIXDEF xp = To_Indx; xp; xp = xp->Next)
+ xp->Invalid = true;
+
+ return false;
+ } // end of InvalidateIndex
+
+/***********************************************************************/
+/* GetTable: makes a new Table Description Block. */
+/***********************************************************************/
+PTDB DOSDEF::GetTable(PGLOBAL g, MODE mode)
+ {
+ // Mapping not used for insert
+ USETEMP tmp = PlgGetUser(g)->UseTemp;
+ bool map = Mapped && mode != MODE_INSERT &&
+ !(tmp != TMP_NO && Recfm == RECFM_VAR
+ && mode == MODE_UPDATE) &&
+ !(tmp == TMP_FORCE &&
+ (mode == MODE_UPDATE || mode == MODE_DELETE));
+ PTXF txfp;
+ PTDBASE tdbp;
+
+ /*********************************************************************/
+ /* Allocate table and file processing class of the proper type. */
+ /* Column blocks will be allocated only when needed. */
+ /*********************************************************************/
+ if (Recfm == RECFM_DBF) {
+ if (map)
+ txfp = new(g) DBMFAM(this);
+ else
+ txfp = new(g) DBFFAM(this);
+
+ tdbp = new(g) TDBFIX(this, txfp);
+ } else if (Recfm != RECFM_VAR && Compressed < 2) {
+ if (Huge)
+ txfp = new(g) BGXFAM(this);
+ else if (map)
+ txfp = new(g) MPXFAM(this);
+#if defined(ZIP_SUPPORT)
+ else if (Compressed)
+ txfp = new(g) ZIXFAM(this);
+#endif // ZIP_SUPPORT
+ else
+ txfp = new(g) FIXFAM(this);
+
+ tdbp = new(g) TDBFIX(this, txfp);
+ } else {
+#if defined(ZIP_SUPPORT)
+ if (Compressed) {
+ if (Compressed == 1)
+ txfp = new(g) ZIPFAM(this);
+ else {
+ strcpy(g->Message, "Compress 2 not supported yet");
+// txfp = new(g) ZLBFAM(defp);
+ return NULL;
+ } // endelse
+
+ } else
+#endif // ZIP_SUPPORT
+ if (map)
+ txfp = new(g) MAPFAM(this);
+ else
+ txfp = new(g) DOSFAM(this);
+
+ // Txfp must be set even for not multiple tables because
+ // it is needed when calling Cardinality in GetBlockValues.
+ tdbp = new(g) TDBDOS(this, txfp);
+ } // endif Recfm
+
+ if (Multiple)
+ tdbp = new(g) TDBMUL(tdbp);
+
+ return tdbp;
+ } // end of GetTable
+
+/* ------------------------ Class TDBDOS ----------------------------- */
+
+/***********************************************************************/
+/* Implementation of the TDBDOS class. This is the common class that */
+/* contain all that is common between the TDBDOS and TDBMAP classes. */
+/***********************************************************************/
+TDBDOS::TDBDOS(PDOSDEF tdp, PTXF txfp) : TDBASE(tdp)
+ {
+ if ((Txfp = txfp))
+ Txfp->SetTdbp(this);
+
+ Lrecl = tdp->Lrecl;
+ AvgLen = tdp->AvgLen;
+ Ftype = tdp->Recfm;
+ To_Line = NULL;
+ Cardinal = -1;
+ } // end of TDBDOS standard constructor
+
+TDBDOS::TDBDOS(PGLOBAL g, PTDBDOS tdbp) : TDBASE(tdbp)
+ {
+ Txfp = (g) ? tdbp->Txfp->Duplicate(g) : tdbp->Txfp;
+ Lrecl = tdbp->Lrecl;
+ AvgLen = tdbp->AvgLen;
+ Ftype = tdbp->Ftype;
+ To_Line = tdbp->To_Line;
+ Cardinal = tdbp->Cardinal;
+ } // end of TDBDOS copy constructor
+
+// Method
+PTDB TDBDOS::CopyOne(PTABS t)
+ {
+ PTDB tp;
+ PDOSCOL cp1, cp2;
+ PGLOBAL g = t->G;
+
+ tp = new(g) TDBDOS(g, this);
+
+ for (cp1 = (PDOSCOL)Columns; cp1; cp1 = (PDOSCOL)cp1->GetNext()) {
+ cp2 = new(g) DOSCOL(cp1, tp); // Make a copy
+ NewPointer(t, cp1, cp2);
+ } // endfor cp1
+
+ return tp;
+ } // end of CopyOne
+
+/***********************************************************************/
+/* Allocate DOS column description block. */
+/***********************************************************************/
+PCOL TDBDOS::MakeCol(PGLOBAL g, PCOLDEF cdp, PCOL cprec, int n)
+ {
+ return new(g) DOSCOL(g, cdp, this, cprec, n);
+ } // end of MakeCol
+
+/***********************************************************************/
+/* Print debug information. */
+/***********************************************************************/
+void TDBDOS::PrintAM(FILE *f, char *m)
+ {
+ fprintf(f, "%s AM(%d): mode=%d\n", m, GetAmType(), Mode);
+
+ if (Txfp->To_File)
+ fprintf(f, "%s File: %s\n", m, Txfp->To_File);
+
+ } // end of PrintAM
+
+/***********************************************************************/
+/* Remake the indexes after the table was modified. */
+/***********************************************************************/
+int TDBDOS::ResetTableOpt(PGLOBAL g, bool dox)
+ {
+ int rc = RC_OK;
+
+ MaxSize = -1; // Size must be recalculated
+ Cardinal = -1; // as well as Cardinality
+
+ if (dox) {
+ // Remake eventual indexes
+ Columns = NULL; // Not used anymore
+ Txfp->Reset(); // New start
+ Use = USE_READY; // So the table can be reopened
+ Mode = MODE_READ; // New mode
+
+ if (!(PlgGetUser(g)->Check & CHK_OPT)) {
+ // After the table was modified the indexes
+ // are invalid and we should mark them as such...
+ rc = ((PDOSDEF)To_Def)->InvalidateIndex(g);
+ } else
+ // ... or we should remake them.
+ rc = MakeIndex(g, NULL, false);
+
+ } // endif dox
+
+ return rc;
+ } // end of ResetTableOpt
+
+/***********************************************************************/
+/* Check whether we have to create/update permanent indexes. */
+/***********************************************************************/
+int TDBDOS::MakeIndex(PGLOBAL g, PIXDEF pxdf, bool add)
+ {
+ int k, n;
+ bool fixed, b = (pxdf != NULL);
+ PCOL *keycols, colp;
+ PIXDEF xdp, sxp = NULL;
+ PKPDEF kdp;
+ PDOSDEF dfp;
+//PCOLDEF cdp;
+ PXINDEX x;
+ PXLOAD pxp;
+ PCATLG cat = PlgGetCatalog(g);
+
+ Mode = MODE_READ;
+ Use = USE_READY;
+ dfp = (PDOSDEF)To_Def;
+ fixed = Cardinality(g) >= 0;
+
+ // Are we are called from CreateTable or CreateIndex?
+ if (pxdf) {
+ if (!add && dfp->GetIndx()) {
+ strcpy(g->Message, MSG(INDX_EXIST_YET));
+ return RC_FX;
+ } // endif To_Indx
+
+ if (add && dfp->GetIndx()) {
+ for (sxp = dfp->GetIndx(); sxp; sxp = sxp->GetNext())
+ if (!stricmp(sxp->GetName(), pxdf->GetName())) {
+ sprintf(g->Message, MSG(INDEX_YET_ON), pxdf->GetName(), Name);
+ return RC_FX;
+ } else if (!sxp->GetNext())
+ break;
+
+ sxp->SetNext(pxdf);
+// first = false;
+ } else
+ dfp->SetIndx(pxdf);
+
+// pxdf->SetDef(dfp);
+ } else if (!(pxdf = dfp->GetIndx()))
+ return RC_INFO; // No index to make
+
+ // Allocate all columns that will be used by indexes.
+ // This must be done before opening the table so specific
+ // column initialization can be done ( in particular by TDBVCT)
+ for (n = 0, xdp = pxdf; xdp; xdp = xdp->GetNext())
+ for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext()) {
+ if (!(colp = ColDB(g, kdp->GetName(), 0))) {
+ sprintf(g->Message, MSG(INDX_COL_NOTIN), kdp->GetName(), Name);
+ goto err;
+ } // endif colp
+
+ colp->InitValue(g);
+ n = max(n, xdp->GetNparts());
+ } // endfor kdp
+
+ keycols = (PCOL*)PlugSubAlloc(g, NULL, n * sizeof(PCOL));
+
+ /*********************************************************************/
+ /* Construct and save the defined indexes. */
+ /*********************************************************************/
+ for (xdp = pxdf; xdp; xdp = xdp->GetNext())
+ if (!OpenDB(g)) {
+ if (xdp->IsAuto() && fixed)
+ // Auto increment key and fixed file: use an XXROW index
+ continue; // XXROW index doesn't need to be made
+
+ n = 0;
+
+ if (sxp)
+ xdp->SetID(sxp->GetID() + 1);
+
+ for (kdp = xdp->GetToKeyParts(); kdp; kdp = kdp->GetNext())
+ keycols[n++] = ColDB(g, kdp->GetName(), 0);
+
+ k = xdp->GetNparts();
+
+ // Make the index and save it
+ if (dfp->Huge)
+ pxp = new(g) XHUGE;
+ else
+ pxp = new(g) XFILE;
+
+ if (k == 1) // Simple index
+ x = new(g) XINDXS(this, xdp, pxp, keycols);
+ else // Multi-Column index
+ x = new(g) XINDEX(this, xdp, pxp, keycols);
+
+ if (!x->Make(g, sxp)) {
+ // Retreive define values from the index
+ xdp->SetMaxSame(x->GetMaxSame());
+ xdp->SetOffset(x->GetDefoff());
+ xdp->SetOffhigh(x->GetDefhigh());
+ xdp->SetSize(x->GetSize());
+
+ // store KXYCOL Mxs in KPARTDEF Mxsame
+ xdp->SetMxsame(x);
+
+#if defined(TRACE)
+ printf("Make done...\n");
+#endif // TRACE
+
+ if (x->GetSize() > 0)
+ sxp = xdp;
+
+ xdp->SetInvalid(false);
+ } else
+ goto err;
+
+ } else
+ break; // Physical table does not exist yet or anymore
+
+ if (Use == USE_OPEN)
+ CloseDB(g);
+
+ return RC_OK;
+
+err:
+ if (sxp)
+ sxp->SetNext(NULL);
+ else
+ dfp->SetIndx(NULL);
+
+ return RC_FX;
+ } // end of MakeIndex
+
+/***********************************************************************/
+/* DOS GetProgMax: get the max value for progress information. */
+/***********************************************************************/
+int TDBDOS::GetProgMax(PGLOBAL g)
+ {
+ return (To_Kindex) ? GetMaxSize(g) : GetFileLength(g);
+ } // end of GetProgMax
+
+/***********************************************************************/
+/* DOS GetProgCur: get the current value for progress information. */
+/***********************************************************************/
+int TDBDOS::GetProgCur(void)
+ {
+ return (To_Kindex) ? To_Kindex->GetCur_K() + 1 : GetRecpos();
+ } // end of GetProgCur
+
+/***********************************************************************/
+/* RowNumber: return the ordinal number of the current row. */
+/***********************************************************************/
+int TDBDOS::RowNumber(PGLOBAL g, bool b)
+ {
+ if (To_Kindex) {
+ /*******************************************************************/
+ /* Don't know how to retrieve RowID from file address. */
+ /*******************************************************************/
+ sprintf(g->Message, MSG(NO_ROWID_FOR_AM),
+ GetAmName(g, Txfp->GetAmType()));
+ return 0;
+ } else
+ return Txfp->GetRowID();
+
+ } // end of RowNumber
+
+/***********************************************************************/
+/* DOS Cardinality: returns table cardinality in number of rows. */
+/* This function can be called with a null argument to test the */
+/* availability of Cardinality implementation (1 yes, 0 no). */
+/***********************************************************************/
+int TDBDOS::Cardinality(PGLOBAL g)
+ {
+ if (!g)
+ return Txfp->Cardinality(g);
+
+ if (Cardinal < 0)
+ Cardinal = Txfp->Cardinality(g);
+
+ return Cardinal;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* DOS GetMaxSize: returns file size estimate in number of lines. */
+/* This function covers variable record length files. */
+/***********************************************************************/
+int TDBDOS::GetMaxSize(PGLOBAL g)
+ {
+ if (MaxSize >= 0)
+ return MaxSize;
+
+ if (!Cardinality(NULL)) {
+ int len = GetFileLength(g);
+
+ if (len >= 0) {
+ if (trace)
+ htrc("Estimating lines len=%d ending=%d\n",
+ len, ((PDOSDEF)To_Def)->Ending);
+
+ /*****************************************************************/
+ /* Estimate the number of lines in the table (if not known) by */
+ /* dividing the file length by the minimum line length assuming */
+ /* only the last column can be of variable length. This will be */
+ /* a ceiling estimate (as last column is never totally absent). */
+ /*****************************************************************/
+ int rec = ((PDOSDEF)To_Def)->Ending; // +2: CRLF +1: LF
+
+ if (AvgLen <= 0) // No given average estimate
+ rec += EstimatedLength(g);
+ else // A lower estimate was given for the average record length
+ rec += (int)AvgLen;
+
+ if (trace)
+ htrc(" Filen=%d min_rec=%d\n", len, rec);
+
+ MaxSize = (len + rec - 1) / rec;
+
+ if (trace)
+ htrc(" Estimated max_K=%d\n", MaxSize);
+
+ } // endif len
+
+ } else
+ MaxSize = Cardinality(g);
+
+ return MaxSize;
+ } // end of GetMaxSize
+
+/***********************************************************************/
+/* DOS EstimatedLength. Returns an estimated minimum line length. */
+/***********************************************************************/
+int TDBDOS::EstimatedLength(PGLOBAL g)
+ {
+ int dep = 0;
+ PCOLDEF cdp = To_Def->GetCols();
+
+ if (!cdp->GetNext()) {
+ // One column table, we are going to return a ridiculous
+ // result if we set dep to 1
+ dep = 1 + cdp->GetLong() / 20; // Why 20 ?????
+ } else for (; cdp; cdp = cdp->GetNext())
+ dep = max(dep, cdp->GetOffset());
+
+ return (int)dep;
+ } // end of Estimated Length
+
+/***********************************************************************/
+/* DOS tables favor the use temporary files for Update. */
+/***********************************************************************/
+bool TDBDOS::IsUsingTemp(PGLOBAL g)
+ {
+ USETEMP usetemp = PlgGetUser(g)->UseTemp;
+
+ return (usetemp == TMP_YES || usetemp == TMP_FORCE ||
+ (usetemp == TMP_AUTO && Mode == MODE_UPDATE));
+ } // end of IsUsingTemp
+
+/***********************************************************************/
+/* DOS Access Method opening routine. */
+/* New method now that this routine is called recursively (last table */
+/* first in reverse order): index blocks are immediately linked to */
+/* join block of next table if it exists or else are discarted. */
+/***********************************************************************/
+bool TDBDOS::OpenDB(PGLOBAL g)
+ {
+ if (trace)
+ htrc("DOS OpenDB: tdbp=%p tdb=R%d use=%d mode=%d\n",
+ this, Tdb_No, Use, Mode);
+
+ if (Use == USE_OPEN) {
+ /*******************************************************************/
+ /* Table already open, just replace it at its beginning. */
+ /*******************************************************************/
+ Txfp->Rewind(); // see comment in Work.log
+
+ if (SkipHeader(g))
+ return true;
+
+ return false;
+ } // endif use
+
+ if (Txfp->Blocked && (Mode == MODE_DELETE ||
+ (Mode == MODE_UPDATE && PlgGetUser(g)->UseTemp != TMP_NO))) {
+ /*******************************************************************/
+ /* Delete is not currently handled in block mode neither Update */
+ /* when using a temporary file. */
+ /*******************************************************************/
+ if (Txfp->GetAmType() == TYPE_AM_MAP && Mode == MODE_DELETE)
+ Txfp = new(g) MAPFAM((PDOSDEF)To_Def);
+#if defined(ZIP_SUPPORT)
+ else if (Txfp->GetAmType() == TYPE_AM_ZIP)
+ Txfp = new(g) ZIPFAM((PDOSDEF)To_Def);
+#endif // ZIP_SUPPORT
+ else if (Txfp->GetAmType() != TYPE_AM_DOS)
+ Txfp = new(g) DOSFAM((PDOSDEF)To_Def);
+
+ Txfp->SetTdbp(this);
+ } // endif Mode
+
+ /*********************************************************************/
+ /* Open according to logical input/output mode required. */
+ /* Use conventionnal input/output functions. */
+ /* Treat files as binary in Delete mode (for line moving) */
+ /*********************************************************************/
+ if (Txfp->OpenTableFile(g))
+ return true;
+
+ Use = USE_OPEN; // Do it now in case we are recursively called
+
+ /*********************************************************************/
+ /* Allocate the line buffer plus a null character. */
+ /*********************************************************************/
+ To_Line = (char*)PlugSubAlloc(g, NULL, Lrecl + 1);
+
+ if (Mode == MODE_INSERT) {
+ // Spaces between fields must be filled with blanks
+ memset(To_Line, ' ', Lrecl);
+ To_Line[Lrecl] = '\0';
+ } else
+ memset(To_Line, 0, Lrecl + 1);
+
+ if (trace)
+ htrc("OpenDos: R%hd mode=%d To_Line=%p\n", Tdb_No, Mode, To_Line);
+
+ if (SkipHeader(g)) // When called from CSV/FMT files
+ return true;
+
+ /*********************************************************************/
+ /* Reset statistics values. */
+ /*********************************************************************/
+ num_read = num_there = num_eq[0] = num_eq[1] = 0;
+ return false;
+ } // end of OpenDB
+
+/***********************************************************************/
+/* ReadDB: Data Base read routine for DOS access method. */
+/***********************************************************************/
+int TDBDOS::ReadDB(PGLOBAL g)
+ {
+ if (trace > 1)
+ htrc("DOS ReadDB: R%d Mode=%d key=%p link=%p Kindex=%p To_Line=%p\n",
+ GetTdb_No(), Mode, To_Key_Col, To_Link, To_Kindex, To_Line);
+
+ if (To_Kindex) {
+ /*******************************************************************/
+ /* Reading is by an index table. */
+ /*******************************************************************/
+ int recpos = To_Kindex->Fetch(g);
+
+ switch (recpos) {
+ case -1: // End of file reached
+ return RC_EF;
+ case -2: // No match for join
+ return RC_NF;
+ case -3: // Same record as last non null one
+ num_there++;
+ return RC_OK;
+ default:
+ /***************************************************************/
+ /* Set the file position according to record to read. */
+ /***************************************************************/
+ if (SetRecpos(g, recpos))
+ return RC_FX;
+
+ if (trace > 1)
+ htrc("File position is now %d\n", GetRecpos());
+
+ if (Mode == MODE_READ)
+ /*************************************************************/
+ /* Defer physical reading until one column setting needs it */
+ /* as it can be a big saving on joins where no other column */
+ /* than the keys are used, so reading is unnecessary. */
+ /*************************************************************/
+ if (Txfp->DeferReading())
+ return RC_OK;
+
+ } // endswitch recpos
+
+ } // endif To_Kindex
+
+ if (trace > 1)
+ htrc(" ReadDB: this=%p To_Line=%p\n", this, To_Line);
+
+ /*********************************************************************/
+ /* Now start the reading process. */
+ /*********************************************************************/
+ return ReadBuffer(g);
+ } // end of ReadDB
+
+/***********************************************************************/
+/* WriteDB: Data Base write routine for DOS access method. */
+/***********************************************************************/
+int TDBDOS::WriteDB(PGLOBAL g)
+ {
+ if (trace > 1)
+ htrc("DOS WriteDB: R%d Mode=%d \n", Tdb_No, Mode);
+
+ if (!Ftype && (Mode == MODE_INSERT || Txfp->GetUseTemp())) {
+ char *p;
+
+ /*******************************************************************/
+ /* Suppress trailing blanks. */
+ /* Also suppress eventual null from last line. */
+ /*******************************************************************/
+ for (p = To_Line + Lrecl -1; p >= To_Line; p--)
+ if (*p && *p != ' ')
+ break;
+
+ *(++p) = '\0';
+ } // endif Mode
+
+ if (trace > 1)
+ htrc("Write: line is='%s'\n", To_Line);
+
+ // Now start the writing process
+ return Txfp->WriteBuffer(g);
+ } // end of WriteDB
+
+/***********************************************************************/
+/* Data Base delete line routine for DOS (and FIX) access method. */
+/* RC_FX means delete all. Nothing to do here (was done at open). */
+/***********************************************************************/
+int TDBDOS::DeleteDB(PGLOBAL g, int irc)
+ {
+ return (irc == RC_FX) ? RC_OK : Txfp->DeleteRecords(g, irc);
+ } // end of DeleteDB
+
+/***********************************************************************/
+/* Data Base close routine for DOS access method. */
+/***********************************************************************/
+void TDBDOS::CloseDB(PGLOBAL g)
+ {
+ if (To_Kindex) {
+ To_Kindex->Close();
+ To_Kindex = NULL;
+ } // endif
+
+ Txfp->CloseTableFile(g);
+ } // end of CloseDB
+
+// ------------------------ DOSCOL functions ----------------------------
+
+/***********************************************************************/
+/* DOSCOL public constructor (also called by MAPCOL). */
+/***********************************************************************/
+DOSCOL::DOSCOL(PGLOBAL g, PCOLDEF cdp, PTDB tp, PCOL cp, int i, PSZ am)
+ : COLBLK(cdp, tp, i)
+ {
+ char *p;
+ int prec = Format.Prec;
+ PTXF txfp = ((PTDBDOS)tp)->Txfp;
+
+ assert(cdp);
+
+ if (cp) {
+ Next = cp->GetNext();
+ cp->SetNext(this);
+ } else {
+ Next = tp->GetColumns();
+ tp->SetColumns(this);
+ } // endif cprec
+
+ // Set additional Dos access method information for column.
+ Deplac = cdp->GetOffset();
+ Long = cdp->GetLong();
+ To_Val = NULL;
+
+ OldVal = NULL; // Currently used only in MinMax
+ Ldz = false;
+ Nod = false;
+ Dcm = -1;
+ p = cdp->GetFmt();
+
+ if (p && IsTypeNum(Buf_Type)) {
+ // Formatted numeric value
+ for (; p && *p && isalpha(*p); p++)
+ switch (toupper(*p)) {
+ case 'Z': // Have leading zeros
+ Ldz = true;
+ break;
+ case 'N': // Have no decimal point
+ Nod = true;
+ break;
+ } // endswitch p
+
+ // Set number of decimal digits
+ Dcm = (*p) ? atoi(p) : GetPrecision();
+ } // endif fmt
+
+ if (trace)
+ htrc(" making new %sCOL C%d %s at %p\n", am, Index, Name, this);
+
+ } // end of DOSCOL constructor
+
+/***********************************************************************/
+/* DOSCOL constructor used for copying columns. */
+/* tdbp is the pointer to the new table descriptor. */
+/***********************************************************************/
+DOSCOL::DOSCOL(DOSCOL *col1, PTDB tdbp) : COLBLK(col1, tdbp)
+ {
+ Deplac = col1->Deplac;
+ Long = col1->Long;
+ To_Val = col1->To_Val;
+ Ldz = col1->Ldz;
+ Nod = col1->Nod;
+ Dcm = col1->Dcm;
+ OldVal = col1->OldVal;
+ Buf = col1->Buf;
+ } // end of DOSCOL copy constructor
+
+/***********************************************************************/
+/* VarSize: This function tells UpdateDB whether or not the block */
+/* optimization file must be redone if this column is updated, even */
+/* it is not sorted or clustered. This applies to the last column of */
+/* a variable length table that is blocked, because if it is updated */
+/* using a temporary file, the block size may be modified. */
+/***********************************************************************/
+bool DOSCOL::VarSize(void)
+ {
+ PTDBDOS tdbp = (PTDBDOS)To_Tdb;
+ PTXF txfp = tdbp->Txfp;
+
+ if (Cdp && !Cdp->GetNext() // Must be the last column
+ && tdbp->Ftype == RECFM_VAR // of a DOS variable length
+ && txfp->Blocked // blocked table
+ && txfp->GetUseTemp()) // using a temporary file.
+ return true;
+ else
+ return false;
+
+ } // end VarSize
+
+/***********************************************************************/
+/* SetBuffer: prepare a column block for write operation. */
+/***********************************************************************/
+bool DOSCOL::SetBuffer(PGLOBAL g, PVAL value, bool ok, bool check)
+ {
+ if (!(To_Val = value)) {
+ sprintf(g->Message, MSG(VALUE_ERROR), Name);
+ return true;
+ } else if (Buf_Type == value->GetType()) {
+ // Values are of the (good) column type
+ if (Buf_Type == TYPE_DATE) {
+ // If any of the date values is formatted
+ // output format must be set for the receiving table
+ if (GetDomain() || ((DTVAL *)value)->IsFormatted())
+ goto newval; // This will make a new value;
+
+ } else if (Buf_Type == TYPE_FLOAT)
+ // Float values must be written with the correct (column) precision
+ // Note: maybe this should be forced by ShowValue instead of this ?
+ ((DFVAL *)value)->SetPrec(GetPrecision());
+
+ Value = value; // Directly access the external value
+ } else {
+ // Values are not of the (good) column type
+ if (check) {
+ sprintf(g->Message, MSG(TYPE_VALUE_ERR), Name,
+ GetTypeName(Buf_Type), GetTypeName(value->GetType()));
+ return true;
+ } // endif check
+
+ newval:
+ if (InitValue(g)) // Allocate the matching value block
+ return true;
+
+ } // endif's Value, Buf_Type
+
+ // Allocate the buffer used in WriteColumn for numeric columns
+ if (IsTypeNum(Buf_Type))
+ Buf = (char*)PlugSubAlloc(g, NULL, max(32, Long + Dcm + 1));
+
+ // Because Colblk's have been made from a copy of the original TDB in
+ // case of Update, we must reset them to point to the original one.
+ if (To_Tdb->GetOrig())
+ To_Tdb = (PTDB)To_Tdb->GetOrig();
+
+ // Set the Column
+ Status = (ok) ? BUF_EMPTY : BUF_NO;
+ return false;
+ } // end of SetBuffer
+
+/***********************************************************************/
+/* ReadColumn: what this routine does is to access the last line */
+/* read from the corresponding table, extract from it the field */
+/* corresponding to this column and convert it to buffer type. */
+/***********************************************************************/
+void DOSCOL::ReadColumn(PGLOBAL g)
+ {
+ char *p;
+ int i, rc;
+ int field;
+ PTDBDOS tdbp = (PTDBDOS)To_Tdb;
+
+ if (trace > 1)
+ htrc(
+ "DOS ReadColumn: col %s R%d coluse=%.4X status=%.4X buf_type=%d\n",
+ Name, tdbp->GetTdb_No(), ColUse, Status, Buf_Type);
+
+ /*********************************************************************/
+ /* If physical reading of the line was deferred, do it now. */
+ /*********************************************************************/
+ if (!tdbp->IsRead())
+ if ((rc = tdbp->ReadBuffer(g)) != RC_OK) {
+ if (rc == RC_EF)
+ sprintf(g->Message, MSG(INV_DEF_READ), rc);
+
+ longjmp(g->jumper[g->jump_level], 11);
+ } // endif
+
+ p = tdbp->To_Line + Deplac;
+ field = Long;
+
+ switch (tdbp->Ftype) {
+ case RECFM_VAR:
+ /*****************************************************************/
+ /* For a variable length file, check if the field exists. */
+ /*****************************************************************/
+ if (strlen(tdbp->To_Line) < (unsigned)Deplac)
+ field = 0;
+
+ case RECFM_FIX: // Fixed length text file
+ case RECFM_DBF: // Fixed length DBase file
+ if (Nod) switch (Buf_Type) {
+ case TYPE_INT:
+ case TYPE_SHORT:
+ Value->SetValue_char(p, field - Dcm);
+ break;
+ case TYPE_FLOAT:
+ Value->SetValue_char(p, field);
+
+ for (i = 0; i < Dcm; i++)
+ ((DFVAL*)Value)->Divide(10.0);
+
+ break;
+ default:
+ Value->SetValue_char(p, field);
+ break;
+ } // endswitch Buf_Type
+
+ else
+ Value->SetValue_char(p, field);
+
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_RECFM), tdbp->Ftype);
+ longjmp(g->jumper[g->jump_level], 34);
+ } // endswitch Ftype
+
+ } // end of ReadColumn
+
+/***********************************************************************/
+/* WriteColumn: what this routine does is to access the last line */
+/* read from the corresponding table, and rewrite the field */
+/* corresponding to this column from the column buffer and type. */
+/***********************************************************************/
+void DOSCOL::WriteColumn(PGLOBAL g)
+ {
+ char *p, *p2, fmt[32];
+ int i, k, len, field;
+ PTDBDOS tdbp = (PTDBDOS)To_Tdb;
+
+ if (trace > 1)
+ htrc("DOS WriteColumn: col %s R%d coluse=%.4X status=%.4X\n",
+ Name, tdbp->GetTdb_No(), ColUse, Status);
+
+ p = tdbp->To_Line + Deplac;
+
+ if (trace > 1)
+ htrc("Lrecl=%d deplac=%d int=%d\n", tdbp->Lrecl, Deplac, Long);
+
+ field = Long;
+
+ if (tdbp->Ftype == RECFM_VAR && tdbp->Mode == MODE_UPDATE) {
+ len = (signed)strlen(tdbp->To_Line);
+
+ if (tdbp->IsUsingTemp(g))
+ // Because of eventual missing field(s) the buffer must be reset
+ memset(tdbp->To_Line + len, ' ', tdbp->Lrecl - len);
+ else
+ // The size actually available must be recalculated
+ field = min(len - Deplac, Long);
+
+ } // endif Ftype
+
+ if (trace > 1)
+ htrc("Long=%d field=%d coltype=%d colval=%p\n",
+ Long, field, Buf_Type, Value);
+
+ /*********************************************************************/
+ /* Get the string representation of Value according to column type. */
+ /*********************************************************************/
+ if (Value != To_Val)
+ Value->SetValue_pval(To_Val, false); // Convert the updated value
+
+ /*********************************************************************/
+ /* This test is only useful for compressed(2) tables. */
+ /*********************************************************************/
+ if (tdbp->Ftype != RECFM_BIN) {
+ if (Ldz || Nod || Dcm >= 0) {
+ switch (Buf_Type) {
+ case TYPE_SHORT:
+ strcpy(fmt, (Ldz) ? "%0*hd" : "%*.hd");
+ i = 0;
+
+ if (Nod)
+ for (; i < Dcm; i++)
+ strcat(fmt, "0");
+
+ len = sprintf(Buf, fmt, field - i, Value->GetShortValue());
+ break;
+ case TYPE_INT:
+ strcpy(fmt, (Ldz) ? "%0*ld" : "%*.ld");
+ i = 0;
+
+ if (Nod)
+ for (; i < Dcm; i++)
+ strcat(fmt, "0");
+
+ len = sprintf(Buf, fmt, field - i, Value->GetIntValue());
+ break;
+ case TYPE_FLOAT:
+ strcpy(fmt, (Ldz) ? "%0*.*lf" : "%*.*lf");
+ sprintf(Buf, fmt, field + ((Nod && Dcm) ? 1 : 0),
+ Dcm, Value->GetFloatValue());
+ len = strlen(Buf);
+
+ if (Nod && Dcm)
+ for (i = k = 0; i < len; i++, k++)
+ if (Buf[i] != ' ') {
+ if (Buf[i] == '.' || Buf[i] == ',')
+ k++;
+
+ Buf[i] = Buf[k];
+ } // endif Buf(i)
+
+ len = strlen(Buf);
+ break;
+ } // endswitch BufType
+
+ p2 = Buf;
+ } else // Standard PlugDB format
+ p2 = Value->ShowValue(Buf, field);
+
+ if (trace)
+ htrc("new length(%p)=%d\n", p2, strlen(p2));
+
+ if ((len = strlen(p2)) > field) {
+ sprintf(g->Message, MSG(VALUE_TOO_LONG), p2, Name, field);
+ longjmp(g->jumper[g->jump_level], 31);
+ } // endif
+
+ if (trace > 1)
+ htrc("buffer=%s\n", p2);
+
+ /*******************************************************************/
+ /* Updating must be done only when not in checking pass. */
+ /*******************************************************************/
+ if (Status) {
+ memset(p, ' ', field);
+ memcpy(p, p2, len);
+
+ if (trace > 1)
+ htrc(" col write: '%.*s'\n", len, p);
+
+ } // endif Use
+
+ } else // BIN compressed table
+ /*******************************************************************/
+ /* Check if updating is Ok, meaning col value is not too long. */
+ /* Updating to be done only during the second pass (Status=true) */
+ /*******************************************************************/
+ if (Value->GetBinValue(p, Long, Status)) {
+ sprintf(g->Message, MSG(BIN_F_TOO_LONG),
+ Name, Value->GetSize(), Long);
+ longjmp(g->jumper[g->jump_level], 31);
+ } // endif
+
+ } // end of WriteColumn
+
+/***********************************************************************/
+/* Make file output of a Dos column descriptor block. */
+/***********************************************************************/
+void DOSCOL::Print(PGLOBAL g, FILE *f, uint n)
+ {
+ COLBLK::Print(g, f, n);
+ } // end of Print
+
+/* ------------------------------------------------------------------- */
+