summaryrefslogtreecommitdiff
path: root/storage/connect/filamtxt.cpp
diff options
context:
space:
mode:
authorOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
committerOlivier Bertrand <bertrandop@gmail.com>2014-03-20 12:05:47 +0100
commit7b400a088d049661b9a4dded385ac78923bb0017 (patch)
treebb3ee7bac4454f66ec6f38cf6683f24bc6c065d4 /storage/connect/filamtxt.cpp
parentd67ad26b33ea16a3b59215ef967bdd9b89345e04 (diff)
parente5729127b8a50a0e553fd8b87b2683e4a684dfcc (diff)
downloadmariadb-git-7b400a088d049661b9a4dded385ac78923bb0017.tar.gz
- MRR + Block Indexing
modified: storage/connect/array.h storage/connect/catalog.h storage/connect/colblk.cpp storage/connect/colblk.h storage/connect/connect.cc storage/connect/connect.h storage/connect/domdoc.h storage/connect/filamap.cpp storage/connect/filamap.h storage/connect/filamdbf.h storage/connect/filamfix.cpp storage/connect/filamfix.h storage/connect/filamtxt.cpp storage/connect/filamtxt.h storage/connect/filamvct.cpp storage/connect/filamvct.h storage/connect/filamzip.cpp storage/connect/filamzip.h storage/connect/filter.cpp storage/connect/filter.h storage/connect/global.h storage/connect/ha_connect.cc storage/connect/ha_connect.h storage/connect/myconn.h storage/connect/plgcnx.h storage/connect/plgdbsem.h storage/connect/plgdbutl.cpp storage/connect/plugutil.c storage/connect/preparse.h storage/connect/reldef.cpp storage/connect/reldef.h storage/connect/tabcol.h storage/connect/tabdos.cpp storage/connect/tabdos.h storage/connect/tabfix.cpp storage/connect/tabfmt.cpp storage/connect/tabfmt.h storage/connect/table.cpp storage/connect/tabmysql.cpp storage/connect/tabmysql.h storage/connect/taboccur.h storage/connect/tabodbc.h storage/connect/tabsys.h storage/connect/tabtbl.h storage/connect/tabutil.h storage/connect/tabvct.cpp storage/connect/tabvct.h storage/connect/tabwmi.h storage/connect/tabxml.h storage/connect/user_connect.cc storage/connect/user_connect.h storage/connect/valblk.cpp storage/connect/valblk.h storage/connect/value.cpp storage/connect/value.h storage/connect/xindex.cpp storage/connect/xindex.h storage/connect/xobject.cpp storage/connect/xobject.h storage/connect/xtable.h
Diffstat (limited to 'storage/connect/filamtxt.cpp')
-rw-r--r--storage/connect/filamtxt.cpp2904
1 files changed, 1442 insertions, 1462 deletions
diff --git a/storage/connect/filamtxt.cpp b/storage/connect/filamtxt.cpp
index d7310f34e6b..24459509e1f 100644
--- a/storage/connect/filamtxt.cpp
+++ b/storage/connect/filamtxt.cpp
@@ -1,1462 +1,1442 @@
-/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
-/* PROGRAM NAME: FILAMTXT */
-/* ------------- */
-/* Version 1.5 */
-/* */
-/* COPYRIGHT: */
-/* ---------- */
-/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
-/* */
-/* WHAT THIS PROGRAM DOES: */
-/* ----------------------- */
-/* This program are the Text file access method classes. */
-/* */
-/***********************************************************************/
-
-/***********************************************************************/
-/* Include relevant sections of the System header files. */
-/***********************************************************************/
-#include "my_global.h"
-#if defined(WIN32)
-#include <io.h>
-#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) || defined(UNIV_LINUX)
-#include <errno.h>
-#include <unistd.h>
-//#if !defined(sun) // Sun has the ftruncate fnc.
-//#define USETEMP // Force copy mode for DELETE
-//#endif // !sun
-#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 "plgdbsem.h"
-#include "filamtxt.h"
-#include "tabdos.h"
-
-#if defined(UNIX) || defined(UNIV_LINUX)
-#include "osutil.h"
-#define _fileno fileno
-#define _O_RDONLY O_RDONLY
-#endif
-
-extern int num_read, num_there, num_eq[2]; // Statistics
-extern "C" int trace;
-
-/* --------------------------- Class TXTFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-TXTFAM::TXTFAM(PDOSDEF tdp)
- {
- Tdbp = NULL;
- To_Fb = NULL;
- To_File = tdp->Fn;
- Lrecl = tdp->Lrecl;
- Placed = false;
- IsRead = true;
- Blocked = false;
- To_Buf = NULL;
- DelBuf = NULL;
- BlkPos = NULL;
- BlkLen = 0;
- Buflen = 0;
- Dbflen = 0;
- Rows = 0;
- DelRows = 0;
- Headlen = 0;
- Block = 0;
- Last = 0;
- Nrec = 1;
- OldBlk = -1;
- CurBlk = -1;
- ReadBlks = 0;
- CurNum = 0;
- Rbuf = 0;
- Modif = 0;
- Blksize = 0;
- Padded = false;
- Eof = tdp->Eof;
- Ending = tdp->Ending;
- CrLf = (char*)(Ending == 2 ? "\r\n" : "\n");
- } // end of TXTFAM standard constructor
-
-TXTFAM::TXTFAM(PTXF txfp)
- {
- Tdbp = txfp->Tdbp;
- To_Fb = txfp->To_Fb;
- To_File = txfp->To_File;
- Lrecl = txfp->Lrecl;
- Placed = txfp->Placed;
- IsRead = txfp->IsRead;
- Blocked = txfp->Blocked;
- To_Buf = txfp->To_Buf;
- DelBuf = txfp->DelBuf;
- BlkPos = txfp->BlkPos;
- BlkLen = txfp->BlkLen;
- Buflen = txfp->Buflen;
- Dbflen = txfp->Dbflen;
- Rows = txfp->Rows;
- DelRows = txfp->DelRows;
- Headlen = txfp->Headlen;
- Block = txfp->Block;
- Last = txfp->Last;
- Nrec = txfp->Nrec;
- OldBlk = txfp->OldBlk;
- CurBlk = txfp->CurBlk;
- ReadBlks = txfp->ReadBlks;
- CurNum = txfp->CurNum;
- Rbuf = txfp->Rbuf;
- Modif = txfp->Modif;
- Blksize = txfp->Blksize;
- Padded = txfp->Padded;
- Eof = txfp->Eof;
- Ending = txfp->Ending;
- } // end of TXTFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void TXTFAM::Reset(void)
- {
- Rows = 0;
- DelRows = 0;
- OldBlk = -1;
- CurBlk = -1;
- ReadBlks = 0;
- CurNum = 0;
- Rbuf = 0;
- Modif = 0;
- Placed = false;
- } // end of Reset
-
-/***********************************************************************/
-/* TXT GetFileLength: returns file size in number of bytes. */
-/***********************************************************************/
-int TXTFAM::GetFileLength(PGLOBAL g)
- {
- char filename[_MAX_PATH];
- int h;
- int len;
-
- PlugSetPath(filename, To_File, Tdbp->GetPath());
- h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
-
- if (trace)
- htrc("GetFileLength: fn=%s h=%d\n", filename, h);
-
- if (h == -1) {
- if (errno != ENOENT) {
- if (trace)
- htrc("%s\n", g->Message);
- len = -1;
- }
- else
- {
- len = 0; // File does not exist yet
- g->Message[0]= '\0';
- }
- } else {
- if ((len = _filelength(h)) < 0)
- sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
-
- if (Eof && len)
- len--; // Do not count the EOF character
-
- close(h);
- } // endif h
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* 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). */
-/* Note: This function is meant only for fixed length files but is */
-/* placed here to be available to FIXFAM and MPXFAM classes. */
-/***********************************************************************/
-int TXTFAM::Cardinality(PGLOBAL g)
- {
- if (g) {
- int card = -1;
- int len = GetFileLength(g);
-
- if (len >= 0) {
- if (Padded && Blksize) {
- if (!(len % Blksize))
- card = (len / Blksize) * Nrec;
- else
- sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
-
- } else {
- if (!(len % Lrecl))
- card = len / (int)Lrecl; // Fixed length file
- else
- sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
-
- } // endif Padded
-
- if (trace)
- htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
- card, len, Lrecl);
-
- } else
- card = 0;
-
- // Set number of blocks for later use
- Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
- return card;
- } else
- return 1;
-
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/* Note: This function is meant only for fixed length files but is */
-/* placed here to be available to FIXFAM and MPXFAM classes. */
-/***********************************************************************/
-int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
- {
- int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
- int size, last = s - blm1 * Nrec;
-
- // Roughly estimate the table size as the sum of blocks
- // that can contain good rows
- for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
-#if defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == blm1) ? last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == blm1) ? last : Nrec;
-#endif // !BLK_INDX
-
- CurBlk = savcur;
- return size;
- } // end of MaxBlkSize
-
-/* --------------------------- Class DOSFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
- {
- To_Fbt = NULL;
- Stream = NULL;
- T_Stream = NULL;
- Fpos = Spos = Tpos = 0;
- UseTemp = false;
- Bin = false;
- } // end of DOSFAM standard constructor
-
-DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
- {
- To_Fbt = tdfp->To_Fbt;
- Stream = tdfp->Stream;
- T_Stream = tdfp->T_Stream;
- Fpos = tdfp->Fpos;
- Spos = tdfp->Spos;
- Tpos = tdfp->Tpos;
- UseTemp = tdfp->UseTemp;
- Bin = tdfp->Bin;
- } // end of DOSFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void DOSFAM::Reset(void)
- {
- TXTFAM::Reset();
- Bin = false;
- Fpos = Tpos = Spos = 0;
- } // end of Reset
-
-/***********************************************************************/
-/* DOS GetFileLength: returns file size in number of bytes. */
-/***********************************************************************/
-int DOSFAM::GetFileLength(PGLOBAL g)
- {
- int len;
-
- if (!Stream)
- len = TXTFAM::GetFileLength(g);
- else
- if ((len = _filelength(_fileno(Stream))) < 0)
- sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
-
- if (trace)
- htrc("File length=%d\n", len);
-
- return len;
- } // end of GetFileLength
-
-/***********************************************************************/
-/* 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 DOSFAM::Cardinality(PGLOBAL g)
- {
- return (g) ? -1 : 0;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/* Note: This function is not really implemented yet. */
-/***********************************************************************/
-int DOSFAM::MaxBlkSize(PGLOBAL g, int s)
- {
- return s;
- } // end of MaxBlkSize
-
-/***********************************************************************/
-/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
-/***********************************************************************/
-bool DOSFAM::OpenTableFile(PGLOBAL g)
- {
- char opmode[4], filename[_MAX_PATH];
-//int ftype = Tdbp->GetFtype();
- MODE mode = Tdbp->Mode;
- PDBUSER dbuserp = PlgGetUser(g);
-
- // This is required when using Unix files under Windows
- Bin = (Ending == 1);
-
- switch (mode) {
- case MODE_READ:
- strcpy(opmode, "r");
- break;
- case MODE_DELETE:
- if (!Tdbp->Next) {
- // Store the number of deleted lines
- DelRows = Cardinality(g);
-
- if (Blocked) {
- // Cardinality must return 0
- Block = 0;
- Last = Nrec;
- } // endif blocked
-
- // This will erase the entire file
- strcpy(opmode, "w");
- Tdbp->ResetSize();
- break;
- } // endif
-
- // Selective delete, pass thru
- Bin = true;
- case MODE_UPDATE:
- if ((UseTemp = Tdbp->IsUsingTemp(g))) {
- strcpy(opmode, "r");
- Bin = true;
- } else
- strcpy(opmode, "r+");
-
- break;
- case MODE_INSERT:
- strcpy(opmode, "a+");
- break;
- default:
- sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
- return true;
- } // endswitch Mode
-
- // For blocked I/O or for moving lines, open the table in binary
- strcat(opmode, (Blocked || Bin) ? "b" : "t");
-
- // Now open the file stream
- PlugSetPath(filename, To_File, Tdbp->GetPath());
-
- if (!(Stream = PlugOpenFile(g, filename, opmode))) {
- if (trace)
- htrc("%s\n", g->Message);
-
- return (mode == MODE_READ && errno == ENOENT)
- ? PushWarning(g, Tdbp) : true;
- } // endif Stream
-
- if (trace)
- htrc("File %s open Stream=%p mode=%s\n", filename, Stream, opmode);
-
- To_Fb = dbuserp->Openlist; // Keep track of File block
-
- /*********************************************************************/
- /* Allocate the line buffer. For mode Delete a bigger buffer has to */
- /* be allocated because is it also used to move lines into the file.*/
- /*********************************************************************/
- return AllocateBuffer(g);
- } // end of OpenTableFile
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete a bigger buffer has to */
-/* be allocated because is it also used to move lines into the file. */
-/***********************************************************************/
-bool DOSFAM::AllocateBuffer(PGLOBAL g)
- {
- MODE mode = Tdbp->Mode;
-
- // Lrecl does not include line ending
- Buflen = Lrecl + Ending + ((Bin) ? 1 : 0);
-
- if (trace)
- htrc("SubAllocating a buffer of %d bytes\n", Buflen);
-
- To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (UseTemp || mode == MODE_DELETE) {
- // Have a big buffer to move lines
- Dbflen = Buflen * DOS_BUFF_LEN;
- DelBuf = PlugSubAlloc(g, NULL, Dbflen);
- } else if (mode == MODE_INSERT) {
- /*******************************************************************/
- /* Prepare the buffer so eventual gaps are filled with blanks. */
- /*******************************************************************/
- memset(To_Buf, ' ', Buflen);
- To_Buf[Buflen - 2] = '\n';
- To_Buf[Buflen - 1] = '\0';
- } // endif's mode
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int DOSFAM::GetRowID(void)
- {
- return Rows;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int DOSFAM::GetPos(void)
- {
- return Fpos;
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: return the position of next record. */
-/***********************************************************************/
-int DOSFAM::GetNextPos(void)
- {
- return ftell(Stream);
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool DOSFAM::SetPos(PGLOBAL g, int pos)
- {
- Fpos = pos;
-
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
- return true;
- } // endif
-
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/***********************************************************************/
-bool DOSFAM::RecordPos(PGLOBAL g)
- {
- if ((Fpos = ftell(Stream)) < 0) {
- sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
- return true;
- } // endif Fpos
-
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int DOSFAM::SkipRecord(PGLOBAL g, bool header)
- {
- PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
-
- // Skip this record
- if (!fgets(To_Buf, Buflen, Stream)) {
- if (feof(Stream))
- return RC_EF;
-
-#if defined(UNIX) || defined(UNIV_LINUX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
- return RC_FX;
- } // endif fgets
-
- // Update progress information
- dup->ProgCur = GetPos();
-
- if (header) {
- // For Delete
- Fpos = ftell(Stream);
-
- if (!UseTemp)
- Tpos = Spos = Fpos; // No need to move header
-
- } // endif header
-
-#if defined(THREAD)
- return RC_NF; // To have progress info
-#else
- return RC_OK; // To loop locally
-#endif
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a text file. */
-/***********************************************************************/
-int DOSFAM::ReadBuffer(PGLOBAL g)
- {
- char *p;
- int rc;
-
- if (!Stream)
- return RC_EF;
-
- if (trace > 1)
- htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
- Tdbp, Tdbp->To_Line, Placed);
-
- if (!Placed) {
- /*******************************************************************/
- /* Record file position in case of UPDATE or DELETE. */
- /*******************************************************************/
-#if defined(BLK_INDX)
- next:
-#endif // BLK_INDX
- if (RecordPos(g))
- return RC_FX;
-
- CurBlk = (int)Rows++;
-
- if (trace > 1)
- htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
-
-#if defined(BLK_INDX)
- /*******************************************************************/
- /* Check whether optimization on ROWID */
- /* can be done, as well as for join as for local filtering. */
- /*******************************************************************/
- switch (Tdbp->TestBlock(g)) {
- case RC_EF:
- return RC_EF;
- case RC_NF:
- // Skip this record
- if ((rc = SkipRecord(g, FALSE)) != RC_OK)
- return rc;
-
- goto next;
- } // endswitch rc
-#endif // BLK_INDX
- } else
- Placed = false;
-
- if (trace > 1)
- htrc(" About to read: stream=%p To_Buf=%p Buflen=%d\n",
- Stream, To_Buf, Buflen);
-
- if (fgets(To_Buf, Buflen, Stream)) {
- p = To_Buf + strlen(To_Buf) - 1;
-
- if (trace > 1)
- htrc(" Read: To_Buf=%p p=%c\n", To_Buf, To_Buf, p);
-
-#if defined(UNIX)
- if (true) {
- // Data files can be imported from Windows (having CRLF)
-#else
- if (Bin) {
- // Data file is read in binary so CRLF remains
-#endif
- if (*p == '\n' || *p == '\r') {
- // is this enough for Unix ???
- *p = '\0'; // Eliminate ending CR or LF character
-
- if (p > To_Buf) {
- // is this enough for Unix ???
- p--;
-
- if (*p == '\n' || *p == '\r')
- *p = '\0'; // Eliminate ending CR or LF character
-
- } // endif To_Buf
-
- } // endif p
-
- } else if (*p == '\n')
- *p = '\0'; // Eliminate ending new-line character
-
- if (trace > 1)
- htrc(" To_Buf='%s'\n", To_Buf);
-
- strcpy(Tdbp->To_Line, To_Buf);
- num_read++;
- rc = RC_OK;
- } else if (feof(Stream)) {
- rc = RC_EF;
- } else {
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
-
- if (trace)
- htrc("%s\n", g->Message);
-
- rc = RC_FX;
- } // endif's fgets
-
- if (trace > 1)
- htrc("ReadBuffer: rc=%d\n", rc);
-
- IsRead = true;
- return rc;
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for DOS access method. */
-/***********************************************************************/
-int DOSFAM::WriteBuffer(PGLOBAL g)
- {
- char *crlf = "\n";
- int curpos = 0;
- bool moved = true;
-
- // T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
- if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
- if (OpenTempFile(g))
- return RC_FX;
-
- } else
- T_Stream = Stream;
-
- if (Tdbp->Mode == MODE_UPDATE) {
- /*******************************************************************/
- /* Here we simply rewrite a record on itself. There are two cases */
- /* were another method should be used, a/ when Update apply to */
- /* the whole file, b/ when updating the last field of a variable */
- /* length file. The method could be to rewrite a new file, then */
- /* to erase the old one and rename the new updated file. */
- /*******************************************************************/
- curpos = ftell(Stream);
-
- if (trace)
- htrc("Last : %d cur: %d\n", Fpos, curpos);
-
- if (UseTemp) {
- /*****************************************************************/
- /* We are using a temporary file. Before writing the updated */
- /* record, we must eventually copy all the intermediate records */
- /* that have not been updated. */
- /*****************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- Spos = curpos; // New start position
- } else
- // Update is directly written back into the file,
- // with this (fast) method, record size cannot change.
- if (fseek(Stream, Fpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif
-
- } // endif mode
-
- /*********************************************************************/
- /* Prepare the write buffer. */
- /*********************************************************************/
-#if defined(WIN32)
- if (Bin)
- crlf = "\r\n";
-#endif // WIN32
- strcat(strcpy(To_Buf, Tdbp->To_Line), crlf);
-
- /*********************************************************************/
- /* Now start the writing process. */
- /*********************************************************************/
- if ((fputs(To_Buf, T_Stream)) == EOF) {
- sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
- return RC_FX;
- } // endif EOF
-
- if (Tdbp->Mode == MODE_UPDATE && moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
- return RC_FX;
- } // endif
-
- if (trace)
- htrc("write done\n");
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Data Base delete line routine for DOS and BLK access methods. */
-/***********************************************************************/
-int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
- {
- bool moved;
- int curpos = ftell(Stream);
-
- /*********************************************************************/
- /* There is an alternative here: */
- /* 1 - use a temporary file in which are copied all not deleted */
- /* lines, at the end the original file will be deleted and */
- /* the temporary file renamed to the original file name. */
- /* 2 - directly move the not deleted lines inside the original */
- /* file, and at the end erase all trailing records. */
- /* This will be experimented, but method 1 must be used for Unix as */
- /* the function needed to erase trailing records is not available. */
- /*********************************************************************/
- if (trace)
- htrc(
- "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
- irc, UseTemp, curpos, Fpos, Tpos, Spos);
-
- if (irc != RC_OK) {
- /*******************************************************************/
- /* EOF: position Fpos at the end-of-file position. */
- /*******************************************************************/
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
-
- if (trace)
- htrc("Fpos placed at file end=%d\n", Fpos);
-
- } // endif irc
-
- if (Tpos == Spos) {
- /*******************************************************************/
- /* First line to delete, Open temporary file. */
- /*******************************************************************/
- if (UseTemp) {
- if (OpenTempFile(g))
- return RC_FX;
-
- } else {
- /*****************************************************************/
- /* Move of eventual preceeding lines is not required here. */
- /* Set the target file as being the source file itself. */
- /* Set the future Tpos, and give Spos a value to block copying. */
- /*****************************************************************/
- T_Stream = Stream;
- Spos = Tpos = Fpos;
- } // endif UseTemp
-
- } // endif Tpos == Spos
-
- /*********************************************************************/
- /* Move any intermediate lines. */
- /*********************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- if (irc == RC_OK) {
- /*******************************************************************/
- /* Reposition the file pointer and set Spos. */
- /*******************************************************************/
- if (!UseTemp || moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif
-
- Spos = GetNextPos(); // New start position
-
- if (trace)
- htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
-
- } else {
- /*******************************************************************/
- /* Last call after EOF has been reached. */
- /* The UseTemp case is treated in CloseTableFile. */
- /*******************************************************************/
- if (!UseTemp) {
- /*****************************************************************/
- /* Because the chsize functionality is only accessible with a */
- /* system call we must close the file and reopen it with the */
- /* open function (_fopen for MS ??) this is still to be checked */
- /* for compatibility with Text files and other OS's. */
- /*****************************************************************/
- char filename[_MAX_PATH];
- int h; // File handle, return code
-
- PlugSetPath(filename, To_File, Tdbp->GetPath());
- /*rc=*/ PlugCloseFile(g, To_Fb);
-
- if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
- return RC_FX;
-
- /*****************************************************************/
- /* Remove extra records. */
- /*****************************************************************/
-#if defined(UNIX)
- if (ftruncate(h, (off_t)Tpos)) {
- sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#else
- if (chsize(h, Tpos)) {
- sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
- close(h);
- return RC_FX;
- } // endif
-#endif
-
- close(h);
-
- if (trace)
- htrc("done, h=%d irc=%d\n", h, irc);
-
- } // endif !UseTemp
-
- } // endif irc
-
- return RC_OK; // All is correct
- } // end of DeleteRecords
-
-/***********************************************************************/
-/* Open a temporary file used while updating or deleting. */
-/***********************************************************************/
-bool DOSFAM::OpenTempFile(PGLOBAL g)
- {
- char tempname[_MAX_PATH];
- bool rc = false;
-
- /*********************************************************************/
- /* Open the temporary file, Spos is at the beginning of file. */
- /*********************************************************************/
- PlugSetPath(tempname, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(tempname, tempname), ".t");
-
- if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
- if (trace)
- htrc("%s\n", g->Message);
-
- rc = true;
- } else
- To_Fbt = PlgGetUser(g)->Openlist;
-
- return rc;
- } // end of OpenTempFile
-
-/***********************************************************************/
-/* Move intermediate deleted or updated lines. */
-/* This works only for file open in binary mode. */
-/***********************************************************************/
-bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
- {
- int n;
- size_t req, len;
-
- for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
- if (!UseTemp || !*b)
- if (fseek(Stream, Spos, SEEK_SET)) {
- sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
- return true;
- } // endif
-
- req = (size_t)min(n, Dbflen);
- len = fread(DelBuf, 1, req, Stream);
-
- if (trace)
- htrc("after read req=%d len=%d\n", req, len);
-
- if (len != req) {
- sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
- return true;
- } // endif len
-
- if (!UseTemp)
- if (fseek(T_Stream, Tpos, SEEK_SET)) {
- sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
- return true;
- } // endif
-
- if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
- sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
- return true;
- } // endif
-
- if (trace)
- htrc("after write pos=%d\n", ftell(Stream));
-
- Tpos += (int)req;
- Spos += (int)req;
-
- if (trace)
- htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
-
- *b = true;
- } // endfor n
-
- return false;
- } // end of MoveIntermediate Lines
-
-/***********************************************************************/
-/* Delete the old file and rename the new temp file. */
-/***********************************************************************/
-int DOSFAM::RenameTempFile(PGLOBAL g)
- {
- char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
- int rc;
-
- if (!To_Fbt)
- return RC_INFO; // Nothing to do ???
-
- // This loop is necessary because, in case of join,
- // To_File can have been open several times.
- for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
- rc = PlugCloseFile(g, fb);
-
- tempname = (char*)To_Fbt->Fname;
- PlugSetPath(filename, To_File, Tdbp->GetPath());
- strcat(PlugRemoveType(filetemp, filename), ".ttt");
- remove(filetemp); // May still be there from previous error
-
- if (rename(filename, filetemp)) { // Save file for security
- sprintf(g->Message, MSG(RENAME_ERROR),
- filename, filetemp, strerror(errno));
- rc = RC_FX;
- } else if (rename(tempname, filename)) {
- sprintf(g->Message, MSG(RENAME_ERROR),
- tempname, filename, strerror(errno));
- rc = rename(filetemp, filename); // Restore saved file
- rc = RC_FX;
- } else if (remove(filetemp)) {
- sprintf(g->Message, MSG(REMOVE_ERROR),
- filetemp, strerror(errno));
- rc = RC_INFO; // Acceptable
- } else
- rc = RC_OK;
-
- return rc;
- } // end of RenameTempFile
-
-/***********************************************************************/
-/* Table file close routine for DOS access method. */
-/***********************************************************************/
-void DOSFAM::CloseTableFile(PGLOBAL g)
- {
- int rc;
-
- if (UseTemp && T_Stream) {
- if (Tdbp->Mode == MODE_UPDATE) {
- // Copy eventually remaining lines
- bool b;
-
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
- rc = MoveIntermediateLines(g, &b);
- } // endif Mode
-
- // Delete the old file and rename the new temp file.
- RenameTempFile(g); // Also close all files
- } else {
- rc = PlugCloseFile(g, To_Fb);
-
- if (trace)
- htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
-
- } // endif UseTemp
-
- Stream = NULL; // So we can know whether table is open
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for DOS access method. */
-/***********************************************************************/
-void DOSFAM::Rewind(void)
- {
- rewind(Stream);
- Rows = 0;
- OldBlk = CurBlk = -1;
- } // end of Rewind
-
-/* --------------------------- Class BLKFAM -------------------------- */
-
-/***********************************************************************/
-/* Constructors. */
-/***********************************************************************/
-BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
- {
- Blocked = true;
- Block = tdp->GetBlock();
- Last = tdp->GetLast();
- Nrec = tdp->GetElemt();
- Closing = false;
-#if defined(BLK_INDX)
- BlkPos = tdp->GetTo_Pos();
-#else // !BLK_INDX
- BlkPos = NULL;
-#endif // !BLK_INDX
- CurLine = NULL;
- NxtLine = NULL;
- OutBuf = NULL;
- } // end of BLKFAM standard constructor
-
-BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
- {
- Closing = txfp->Closing;
- CurLine = txfp->CurLine;
- NxtLine = txfp->NxtLine;
- OutBuf = txfp->OutBuf;
- } // end of BLKFAM copy constructor
-
-/***********************************************************************/
-/* Reset: reset position values at the beginning of file. */
-/***********************************************************************/
-void BLKFAM::Reset(void)
- {
- DOSFAM::Reset();
- Closing = false;
- } // end of Reset
-
-/***********************************************************************/
-/* 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 BLKFAM::Cardinality(PGLOBAL g)
- {
- // Should not be called in this version
- return (g) ? -1 : 0;
-//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
- } // end of Cardinality
-
-/***********************************************************************/
-/* Use BlockTest to reduce the table estimated size. */
-/***********************************************************************/
-int BLKFAM::MaxBlkSize(PGLOBAL g, int s)
- {
- int rc = RC_OK, savcur = CurBlk;
- int size;
-
- // Roughly estimate the table size as the sum of blocks
- // that can contain good rows
- for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
-#if defined(BLK_INDX)
- if ((rc = Tdbp->TestBlock(g)) == RC_OK)
- size += (CurBlk == Block - 1) ? Last : Nrec;
- else if (rc == RC_EF)
- break;
-#else // !BLK_INDX
- size += (CurBlk == Block - 1) ? Last : Nrec;
-#endif // !BLK_INDX
-
- CurBlk = savcur;
- return size;
- } // end of MaxBlkSize
-
-/***********************************************************************/
-/* Allocate the line buffer. For mode Delete or when a temp file is */
-/* used another big buffer has to be allocated because is it used */
-/* to move or update the lines into the (temp) file. */
-/***********************************************************************/
-bool BLKFAM::AllocateBuffer(PGLOBAL g)
- {
- int len;
- MODE mode = Tdbp->GetMode();
-
- // For variable length files, Lrecl does not include CRLF
- len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
- Buflen = len * Nrec;
- CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
-
- if (UseTemp || mode == MODE_DELETE) {
- if (mode == MODE_UPDATE)
- OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
-
- Dbflen = Buflen;
- DelBuf = PlugSubAlloc(g, NULL, Dbflen);
- } else if (mode == MODE_INSERT)
- Rbuf = Nrec; // To be used by WriteDB
-
- return false;
- } // end of AllocateBuffer
-
-/***********************************************************************/
-/* GetRowID: return the RowID of last read record. */
-/***********************************************************************/
-int BLKFAM::GetRowID(void)
- {
- return CurNum + Nrec * CurBlk + 1;
- } // end of GetRowID
-
-/***********************************************************************/
-/* GetPos: return the position of last read record. */
-/***********************************************************************/
-int BLKFAM::GetPos(void)
- {
- return (CurNum + Nrec * CurBlk); // Computed file index
- } // end of GetPos
-
-/***********************************************************************/
-/* GetNextPos: called by DeleteRecords. */
-/***********************************************************************/
-int BLKFAM::GetNextPos(void)
- {
- return Fpos + NxtLine - CurLine;
- } // end of GetNextPos
-
-/***********************************************************************/
-/* SetPos: Replace the table at the specified position. */
-/***********************************************************************/
-bool BLKFAM::SetPos(PGLOBAL g, int pos)
- {
- if (pos < 0) {
- strcpy(g->Message, MSG(INV_REC_POS));
- return true;
- } // endif recpos
-
- CurBlk = pos / Nrec;
- CurNum = pos % Nrec;
-#if defined(_DEBUG)
- num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
-#endif
-
- // Indicate the table position was externally set
- Placed = true;
- return false;
- } // end of SetPos
-
-/***********************************************************************/
-/* Record file position in case of UPDATE or DELETE. */
-/* Not used yet for blocked tables. */
-/***********************************************************************/
-bool BLKFAM::RecordPos(PGLOBAL g)
- {
- Fpos = (CurNum + Nrec * CurBlk); // Computed file index
- return false;
- } // end of RecordPos
-
-/***********************************************************************/
-/* Skip one record in file. */
-/***********************************************************************/
-int BLKFAM::SkipRecord(PGLOBAL g, bool header)
- {
- if (header) {
- // For Delete
- Fpos = BlkPos[0]; // First block starts after the header
-
- if (!UseTemp)
- Tpos = Spos = Fpos; // No need to move header
-
- } // endif header
-
- OldBlk = -2; // To force fseek on first block
- return RC_OK;
- } // end of SkipRecord
-
-/***********************************************************************/
-/* ReadBuffer: Read one line for a text file. */
-/***********************************************************************/
-int BLKFAM::ReadBuffer(PGLOBAL g)
- {
-#if defined(BLK_INDX)
- int i, n, rc = RC_OK;
-
- /*********************************************************************/
- /* Sequential reading when Placed is not true. */
- /*********************************************************************/
- if (Placed) {
- Placed = false;
- } else if (++CurNum < Rbuf) {
- CurLine = NxtLine;
-
- // Get the position of the next line in the buffer
- while (*NxtLine++ != '\n') ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- goto fin;
- } else if (Rbuf < Nrec && CurBlk != -1) {
- return RC_EF;
- } else {
- /*******************************************************************/
- /* New block. */
- /*******************************************************************/
- CurNum = 0;
-
- next:
- if (++CurBlk >= Block)
- return RC_EF;
-
- /*******************************************************************/
- /* Before reading a new block, check whether block optimization */
- /* can be done, as well as for join as for local filtering. */
- /*******************************************************************/
- switch (Tdbp->TestBlock(g)) {
- case RC_EF:
- return RC_EF;
- case RC_NF:
- goto next;
- } // endswitch rc
-
- } // endif's
-
- if (OldBlk == CurBlk)
- goto ok; // Block is already there
-
- // fseek is required only in non sequential reading
- if (CurBlk != OldBlk + 1)
- if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
- sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
- return RC_FX;
- } // endif fseek
-
- // Calculate the length of block to read
- BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
-
- if (trace)
- htrc("File position is now %d\n", ftell(Stream));
-
- // Read the entire next block
- n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
-
- if (n == BlkLen) {
-// ReadBlks++;
- num_read++;
- Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
-
- ok:
- rc = RC_OK;
-
- // Get the position of the current line
- for (i = 0, CurLine = To_Buf; i < CurNum; i++)
- while (*CurLine++ != '\n') ; // What about Unix ???
-
- // Now get the position of the next line
- for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
-
- // Set caller line buffer
- n = NxtLine - CurLine - Ending;
- memcpy(Tdbp->GetLine(), CurLine, n);
- Tdbp->GetLine()[n] = '\0';
- } else if (feof(Stream)) {
- rc = RC_EF;
- } else {
-#if defined(UNIX)
- sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
-#else
- sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
-#endif
-
- if (trace)
- htrc("%s\n", g->Message);
-
- return RC_FX;
- } // endelse
-
- OldBlk = CurBlk; // Last block actually read
- IsRead = true; // Is read indeed
-
- fin:
- // Store the current record file position for Delete and Update
- Fpos = BlkPos[CurBlk] + CurLine - To_Buf;
- return rc;
-#else // !BLK_POS
- strcpy(g->Message, "This AM cannot be used in this version");
- return RC_FX;
-#endif // !BLK_POS
- } // end of ReadBuffer
-
-/***********************************************************************/
-/* WriteBuffer: File write routine for the blocked DOS access method. */
-/* Update is directly written back into the file, */
-/* with this (fast) method, record size cannot change. */
-/***********************************************************************/
-int BLKFAM::WriteBuffer(PGLOBAL g)
- {
- if (Tdbp->GetMode() == MODE_INSERT) {
- /*******************************************************************/
- /* In Insert mode, blocks are added sequentially to the file end. */
- /*******************************************************************/
- if (!Closing) { // Add line to the write buffer
- strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
-
- if (++CurNum != Rbuf) {
- CurLine += strlen(CurLine);
- return RC_OK; // We write only full blocks
- } // endif CurNum
-
- } // endif Closing
-
- // Now start the writing process.
- NxtLine = CurLine + strlen(CurLine);
- BlkLen = NxtLine - To_Buf;
-
- if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- Closing = true; // To tell CloseDB about a Write error
- return RC_FX;
- } // endif size
-
- CurBlk++;
- CurNum = 0;
- CurLine = To_Buf;
- } else {
- /*******************************************************************/
- /* Mode == MODE_UPDATE. */
- /*******************************************************************/
- char *crlf;
- size_t len;
- int curpos = ftell(Stream);
- bool moved = true;
-
- // T_Stream is the temporary stream or the table file stream itself
- if (!T_Stream)
- if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
- if (OpenTempFile(g))
- return RC_FX;
-
- } else
- T_Stream = Stream;
-
- if (UseTemp) {
- /*****************************************************************/
- /* We are using a temporary file. Before writing the updated */
- /* record, we must eventually copy all the intermediate records */
- /* that have not been updated. */
- /*****************************************************************/
- if (MoveIntermediateLines(g, &moved))
- return RC_FX;
-
- Spos = GetNextPos(); // New start position
-
- // Prepare the output buffer
-#if defined(WIN32)
- crlf = "\r\n";
-#else
- crlf = "\n";
-#endif // WIN32
- strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
- len = strlen(OutBuf);
- } else {
- if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
- sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
- return RC_FX;
- } // endif fseek
-
- // Replace the line inside read buffer (length has not changed)
- memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
- OutBuf = CurLine;
- len = (size_t)(NxtLine - CurLine);
- } // endif UseTemp
-
- if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
- sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
- return RC_FX;
- } // endif fwrite
-
- if (moved)
- if (fseek(Stream, curpos, SEEK_SET)) {
- sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
- return RC_FX;
- } // endif
-
- } // endif Mode
-
- return RC_OK;
- } // end of WriteBuffer
-
-/***********************************************************************/
-/* Table file close routine for DOS access method. */
-/***********************************************************************/
-void BLKFAM::CloseTableFile(PGLOBAL g)
- {
- int rc, wrc = RC_OK;
-
- if (UseTemp && T_Stream) {
- if (Tdbp->GetMode() == MODE_UPDATE) {
- // Copy eventually remaining lines
- bool b;
-
- fseek(Stream, 0, SEEK_END);
- Fpos = ftell(Stream);
- rc = MoveIntermediateLines(g, &b);
- } else
- rc = RC_OK;
-
- if (rc == RC_OK)
- // Delete the old file and rename the new temp file.
- rc = RenameTempFile(g); // Also close all files
- else
- rc = PlugCloseFile(g, To_Fb);
-
- } else {
- // Closing is True if last Write was in error
- if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
- // Some more inserted lines remain to be written
- Rbuf = CurNum--;
- Closing = true;
- wrc = WriteBuffer(g);
- } else if (Modif && !Closing) {
- // Last updated block remains to be written
- Closing = true;
- wrc = ReadBuffer(g);
- } // endif's
-
- rc = PlugCloseFile(g, To_Fb);
-
- if (trace)
- htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
- To_File, Tdbp->GetMode(), wrc, rc);
-
- } // endif UseTemp
-
- Stream = NULL; // So we can know whether table is open
- } // end of CloseTableFile
-
-/***********************************************************************/
-/* Rewind routine for DOS access method. */
-/* Note: commenting out OldBlk = -1 has two advantages: */
-/* 1 - It forces fseek on first block, thus suppressing the need to */
-/* rewind the file, anyway unuseful when second pass if indexed. */
-/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
-/***********************************************************************/
-void BLKFAM::Rewind(void)
- {
-//rewind(Stream); will be placed by fseek
- CurBlk = -1;
- CurNum = Rbuf;
-//OldBlk = -1; commented out in case we reuse last read block
-//Rbuf = 0; commented out in case we reuse last read block
- } // end of Rewind
-
+/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
+/* PROGRAM NAME: FILAMTXT */
+/* ------------- */
+/* Version 1.5 */
+/* */
+/* COPYRIGHT: */
+/* ---------- */
+/* (C) Copyright to the author Olivier BERTRAND 2005-2014 */
+/* */
+/* WHAT THIS PROGRAM DOES: */
+/* ----------------------- */
+/* This program are the Text file access method classes. */
+/* */
+/***********************************************************************/
+
+/***********************************************************************/
+/* Include relevant sections of the System header files. */
+/***********************************************************************/
+#include "my_global.h"
+#if defined(WIN32)
+#include <io.h>
+#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) || defined(UNIV_LINUX)
+#include <errno.h>
+#include <unistd.h>
+//#if !defined(sun) // Sun has the ftruncate fnc.
+//#define USETEMP // Force copy mode for DELETE
+//#endif // !sun
+#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 "plgdbsem.h"
+#include "filamtxt.h"
+#include "tabdos.h"
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+#include "osutil.h"
+#define _fileno fileno
+#define _O_RDONLY O_RDONLY
+#endif
+
+extern int num_read, num_there, num_eq[2]; // Statistics
+extern "C" int trace;
+
+/* --------------------------- Class TXTFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+TXTFAM::TXTFAM(PDOSDEF tdp)
+ {
+ Tdbp = NULL;
+ To_Fb = NULL;
+ To_File = tdp->Fn;
+ Lrecl = tdp->Lrecl;
+ Placed = false;
+ IsRead = true;
+ Blocked = false;
+ To_Buf = NULL;
+ DelBuf = NULL;
+ BlkPos = NULL;
+ BlkLen = 0;
+ Buflen = 0;
+ Dbflen = 0;
+ Rows = 0;
+ DelRows = 0;
+ Headlen = 0;
+ Block = 0;
+ Last = 0;
+ Nrec = 1;
+ OldBlk = -1;
+ CurBlk = -1;
+ ReadBlks = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ Modif = 0;
+ Blksize = 0;
+ Padded = false;
+ Eof = tdp->Eof;
+ Ending = tdp->Ending;
+ CrLf = (char*)(Ending == 2 ? "\r\n" : "\n");
+ } // end of TXTFAM standard constructor
+
+TXTFAM::TXTFAM(PTXF txfp)
+ {
+ Tdbp = txfp->Tdbp;
+ To_Fb = txfp->To_Fb;
+ To_File = txfp->To_File;
+ Lrecl = txfp->Lrecl;
+ Placed = txfp->Placed;
+ IsRead = txfp->IsRead;
+ Blocked = txfp->Blocked;
+ To_Buf = txfp->To_Buf;
+ DelBuf = txfp->DelBuf;
+ BlkPos = txfp->BlkPos;
+ BlkLen = txfp->BlkLen;
+ Buflen = txfp->Buflen;
+ Dbflen = txfp->Dbflen;
+ Rows = txfp->Rows;
+ DelRows = txfp->DelRows;
+ Headlen = txfp->Headlen;
+ Block = txfp->Block;
+ Last = txfp->Last;
+ Nrec = txfp->Nrec;
+ OldBlk = txfp->OldBlk;
+ CurBlk = txfp->CurBlk;
+ ReadBlks = txfp->ReadBlks;
+ CurNum = txfp->CurNum;
+ Rbuf = txfp->Rbuf;
+ Modif = txfp->Modif;
+ Blksize = txfp->Blksize;
+ Padded = txfp->Padded;
+ Eof = txfp->Eof;
+ Ending = txfp->Ending;
+ } // end of TXTFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void TXTFAM::Reset(void)
+ {
+ Rows = 0;
+ DelRows = 0;
+ OldBlk = -1;
+ CurBlk = -1;
+ ReadBlks = 0;
+ CurNum = 0;
+ Rbuf = 0;
+ Modif = 0;
+ Placed = false;
+ } // end of Reset
+
+/***********************************************************************/
+/* TXT GetFileLength: returns file size in number of bytes. */
+/***********************************************************************/
+int TXTFAM::GetFileLength(PGLOBAL g)
+ {
+ char filename[_MAX_PATH];
+ int h;
+ int len;
+
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+ h= global_open(g, MSGID_OPEN_MODE_STRERROR, filename, _O_RDONLY);
+
+ if (trace)
+ htrc("GetFileLength: fn=%s h=%d\n", filename, h);
+
+ if (h == -1) {
+ if (errno != ENOENT) {
+ if (trace)
+ htrc("%s\n", g->Message);
+ len = -1;
+ }
+ else
+ {
+ len = 0; // File does not exist yet
+ g->Message[0]= '\0';
+ }
+ } else {
+ if ((len = _filelength(h)) < 0)
+ sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", filename);
+
+ if (Eof && len)
+ len--; // Do not count the EOF character
+
+ close(h);
+ } // endif h
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* 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). */
+/* Note: This function is meant only for fixed length files but is */
+/* placed here to be available to FIXFAM and MPXFAM classes. */
+/***********************************************************************/
+int TXTFAM::Cardinality(PGLOBAL g)
+ {
+ if (g) {
+ int card = -1;
+ int len = GetFileLength(g);
+
+ if (len >= 0) {
+ if (Padded && Blksize) {
+ if (!(len % Blksize))
+ card = (len / Blksize) * Nrec;
+ else
+ sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
+
+ } else {
+ if (!(len % Lrecl))
+ card = len / (int)Lrecl; // Fixed length file
+ else
+ sprintf(g->Message, MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
+
+ } // endif Padded
+
+ if (trace)
+ htrc(" Computed max_K=%d Filen=%d lrecl=%d\n",
+ card, len, Lrecl);
+
+ } else
+ card = 0;
+
+ // Set number of blocks for later use
+ Block = (card > 0) ? (card + Nrec - 1) / Nrec : 0;
+ return card;
+ } else
+ return 1;
+
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/* Note: This function is meant only for fixed length files but is */
+/* placed here to be available to FIXFAM and MPXFAM classes. */
+/***********************************************************************/
+int TXTFAM::MaxBlkSize(PGLOBAL g, int s)
+ {
+ int rc = RC_OK, savcur = CurBlk, blm1 = Block - 1;
+ int size, last = s - blm1 * Nrec;
+
+ // Roughly estimate the table size as the sum of blocks
+ // that can contain good rows
+ for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
+ if ((rc = Tdbp->TestBlock(g)) == RC_OK)
+ size += (CurBlk == blm1) ? last : Nrec;
+ else if (rc == RC_EF)
+ break;
+
+ CurBlk = savcur;
+ return size;
+ } // end of MaxBlkSize
+
+/* --------------------------- Class DOSFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
+ {
+ To_Fbt = NULL;
+ Stream = NULL;
+ T_Stream = NULL;
+ Fpos = Spos = Tpos = 0;
+ UseTemp = false;
+ Bin = false;
+ } // end of DOSFAM standard constructor
+
+DOSFAM::DOSFAM(PDOSFAM tdfp) : TXTFAM(tdfp)
+ {
+ To_Fbt = tdfp->To_Fbt;
+ Stream = tdfp->Stream;
+ T_Stream = tdfp->T_Stream;
+ Fpos = tdfp->Fpos;
+ Spos = tdfp->Spos;
+ Tpos = tdfp->Tpos;
+ UseTemp = tdfp->UseTemp;
+ Bin = tdfp->Bin;
+ } // end of DOSFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void DOSFAM::Reset(void)
+ {
+ TXTFAM::Reset();
+ Bin = false;
+ Fpos = Tpos = Spos = 0;
+ } // end of Reset
+
+/***********************************************************************/
+/* DOS GetFileLength: returns file size in number of bytes. */
+/***********************************************************************/
+int DOSFAM::GetFileLength(PGLOBAL g)
+ {
+ int len;
+
+ if (!Stream)
+ len = TXTFAM::GetFileLength(g);
+ else
+ if ((len = _filelength(_fileno(Stream))) < 0)
+ sprintf(g->Message, MSG(FILELEN_ERROR), "_filelength", To_File);
+
+ if (trace)
+ htrc("File length=%d\n", len);
+
+ return len;
+ } // end of GetFileLength
+
+/***********************************************************************/
+/* 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 DOSFAM::Cardinality(PGLOBAL g)
+ {
+ return (g) ? -1 : 0;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/* Note: This function is not really implemented yet. */
+/***********************************************************************/
+int DOSFAM::MaxBlkSize(PGLOBAL g, int s)
+ {
+ return s;
+ } // end of MaxBlkSize
+
+/***********************************************************************/
+/* OpenTableFile: Open a DOS/UNIX table file using C standard I/Os. */
+/***********************************************************************/
+bool DOSFAM::OpenTableFile(PGLOBAL g)
+ {
+ char opmode[4], filename[_MAX_PATH];
+//int ftype = Tdbp->GetFtype();
+ MODE mode = Tdbp->Mode;
+ PDBUSER dbuserp = PlgGetUser(g);
+
+ // This is required when using Unix files under Windows
+ Bin = (Ending == 1);
+
+ switch (mode) {
+ case MODE_READ:
+ strcpy(opmode, "r");
+ break;
+ case MODE_DELETE:
+ if (!Tdbp->Next) {
+ // Store the number of deleted lines
+ DelRows = Cardinality(g);
+
+ if (Blocked) {
+ // Cardinality must return 0
+ Block = 0;
+ Last = Nrec;
+ } // endif blocked
+
+ // This will erase the entire file
+ strcpy(opmode, "w");
+ Tdbp->ResetSize();
+ break;
+ } // endif
+
+ // Selective delete, pass thru
+ Bin = true;
+ case MODE_UPDATE:
+ if ((UseTemp = Tdbp->IsUsingTemp(g))) {
+ strcpy(opmode, "r");
+ Bin = true;
+ } else
+ strcpy(opmode, "r+");
+
+ break;
+ case MODE_INSERT:
+ strcpy(opmode, "a+");
+ break;
+ default:
+ sprintf(g->Message, MSG(BAD_OPEN_MODE), mode);
+ return true;
+ } // endswitch Mode
+
+ // For blocked I/O or for moving lines, open the table in binary
+ strcat(opmode, (Blocked || Bin) ? "b" : "t");
+
+ // Now open the file stream
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+
+ if (!(Stream = PlugOpenFile(g, filename, opmode))) {
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return (mode == MODE_READ && errno == ENOENT)
+ ? PushWarning(g, Tdbp) : true;
+ } // endif Stream
+
+ if (trace)
+ htrc("File %s open Stream=%p mode=%s\n", filename, Stream, opmode);
+
+ To_Fb = dbuserp->Openlist; // Keep track of File block
+
+ /*********************************************************************/
+ /* Allocate the line buffer. For mode Delete a bigger buffer has to */
+ /* be allocated because is it also used to move lines into the file.*/
+ /*********************************************************************/
+ return AllocateBuffer(g);
+ } // end of OpenTableFile
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete a bigger buffer has to */
+/* be allocated because is it also used to move lines into the file. */
+/***********************************************************************/
+bool DOSFAM::AllocateBuffer(PGLOBAL g)
+ {
+ MODE mode = Tdbp->Mode;
+
+ // Lrecl does not include line ending
+ Buflen = Lrecl + Ending + ((Bin) ? 1 : 0);
+
+ if (trace)
+ htrc("SubAllocating a buffer of %d bytes\n", Buflen);
+
+ To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (UseTemp || mode == MODE_DELETE) {
+ // Have a big buffer to move lines
+ Dbflen = Buflen * DOS_BUFF_LEN;
+ DelBuf = PlugSubAlloc(g, NULL, Dbflen);
+ } else if (mode == MODE_INSERT) {
+ /*******************************************************************/
+ /* Prepare the buffer so eventual gaps are filled with blanks. */
+ /*******************************************************************/
+ memset(To_Buf, ' ', Buflen);
+ To_Buf[Buflen - 2] = '\n';
+ To_Buf[Buflen - 1] = '\0';
+ } // endif's mode
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int DOSFAM::GetRowID(void)
+ {
+ return Rows;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int DOSFAM::GetPos(void)
+ {
+ return Fpos;
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: return the position of next record. */
+/***********************************************************************/
+int DOSFAM::GetNextPos(void)
+ {
+ return ftell(Stream);
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool DOSFAM::SetPos(PGLOBAL g, int pos)
+ {
+ Fpos = pos;
+
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), Fpos);
+ return true;
+ } // endif
+
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/***********************************************************************/
+bool DOSFAM::RecordPos(PGLOBAL g)
+ {
+ if ((Fpos = ftell(Stream)) < 0) {
+ sprintf(g->Message, MSG(FTELL_ERROR), 0, strerror(errno));
+ return true;
+ } // endif Fpos
+
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int DOSFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+
+ // Skip this record
+ if (!fgets(To_Buf, Buflen, Stream)) {
+ if (feof(Stream))
+ return RC_EF;
+
+#if defined(UNIX) || defined(UNIV_LINUX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+ return RC_FX;
+ } // endif fgets
+
+ // Update progress information
+ dup->ProgCur = GetPos();
+
+ if (header) {
+ // For Delete
+ Fpos = ftell(Stream);
+
+ if (!UseTemp)
+ Tpos = Spos = Fpos; // No need to move header
+
+ } // endif header
+
+#if defined(THREAD)
+ return RC_NF; // To have progress info
+#else
+ return RC_OK; // To loop locally
+#endif
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a text file. */
+/***********************************************************************/
+int DOSFAM::ReadBuffer(PGLOBAL g)
+ {
+ char *p;
+ int rc;
+
+ if (!Stream)
+ return RC_EF;
+
+ if (trace > 1)
+ htrc("ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
+ Tdbp, Tdbp->To_Line, Placed);
+
+ if (!Placed) {
+ /*******************************************************************/
+ /* Record file position in case of UPDATE or DELETE. */
+ /*******************************************************************/
+ next:
+ if (RecordPos(g))
+ return RC_FX;
+
+ CurBlk = (int)Rows++;
+
+ if (trace > 1)
+ htrc("ReadBuffer: CurBlk=%d\n", CurBlk);
+
+ /*******************************************************************/
+ /* Check whether optimization on ROWID */
+ /* can be done, as well as for join as for local filtering. */
+ /*******************************************************************/
+ switch (Tdbp->TestBlock(g)) {
+ case RC_EF:
+ return RC_EF;
+ case RC_NF:
+ // Skip this record
+ if ((rc = SkipRecord(g, FALSE)) != RC_OK)
+ return rc;
+
+ goto next;
+ } // endswitch rc
+
+ } else
+ Placed = false;
+
+ if (trace > 1)
+ htrc(" About to read: stream=%p To_Buf=%p Buflen=%d\n",
+ Stream, To_Buf, Buflen);
+
+ if (fgets(To_Buf, Buflen, Stream)) {
+ p = To_Buf + strlen(To_Buf) - 1;
+
+ if (trace > 1)
+ htrc(" Read: To_Buf=%p p=%c\n", To_Buf, To_Buf, p);
+
+#if defined(UNIX)
+ if (true) {
+ // Data files can be imported from Windows (having CRLF)
+#else
+ if (Bin) {
+ // Data file is read in binary so CRLF remains
+#endif
+ if (*p == '\n' || *p == '\r') {
+ // is this enough for Unix ???
+ *p = '\0'; // Eliminate ending CR or LF character
+
+ if (p > To_Buf) {
+ // is this enough for Unix ???
+ p--;
+
+ if (*p == '\n' || *p == '\r')
+ *p = '\0'; // Eliminate ending CR or LF character
+
+ } // endif To_Buf
+
+ } // endif p
+
+ } else if (*p == '\n')
+ *p = '\0'; // Eliminate ending new-line character
+
+ if (trace > 1)
+ htrc(" To_Buf='%s'\n", To_Buf);
+
+ strcpy(Tdbp->To_Line, To_Buf);
+ num_read++;
+ rc = RC_OK;
+ } else if (feof(Stream)) {
+ rc = RC_EF;
+ } else {
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(0));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ rc = RC_FX;
+ } // endif's fgets
+
+ if (trace > 1)
+ htrc("ReadBuffer: rc=%d\n", rc);
+
+ IsRead = true;
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for DOS access method. */
+/***********************************************************************/
+int DOSFAM::WriteBuffer(PGLOBAL g)
+ {
+ char *crlf = "\n";
+ int curpos = 0;
+ bool moved = true;
+
+ // T_Stream is the temporary stream or the table file stream itself
+ if (!T_Stream)
+ if (UseTemp && Tdbp->Mode == MODE_UPDATE) {
+ if (OpenTempFile(g))
+ return RC_FX;
+
+ } else
+ T_Stream = Stream;
+
+ if (Tdbp->Mode == MODE_UPDATE) {
+ /*******************************************************************/
+ /* Here we simply rewrite a record on itself. There are two cases */
+ /* were another method should be used, a/ when Update apply to */
+ /* the whole file, b/ when updating the last field of a variable */
+ /* length file. The method could be to rewrite a new file, then */
+ /* to erase the old one and rename the new updated file. */
+ /*******************************************************************/
+ curpos = ftell(Stream);
+
+ if (trace)
+ htrc("Last : %d cur: %d\n", Fpos, curpos);
+
+ if (UseTemp) {
+ /*****************************************************************/
+ /* We are using a temporary file. Before writing the updated */
+ /* record, we must eventually copy all the intermediate records */
+ /* that have not been updated. */
+ /*****************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ Spos = curpos; // New start position
+ } else
+ // Update is directly written back into the file,
+ // with this (fast) method, record size cannot change.
+ if (fseek(Stream, Fpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif
+
+ } // endif mode
+
+ /*********************************************************************/
+ /* Prepare the write buffer. */
+ /*********************************************************************/
+#if defined(WIN32)
+ if (Bin)
+ crlf = "\r\n";
+#endif // WIN32
+ strcat(strcpy(To_Buf, Tdbp->To_Line), crlf);
+
+ /*********************************************************************/
+ /* Now start the writing process. */
+ /*********************************************************************/
+ if ((fputs(To_Buf, T_Stream)) == EOF) {
+ sprintf(g->Message, MSG(FPUTS_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif EOF
+
+ if (Tdbp->Mode == MODE_UPDATE && moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif
+
+ if (trace)
+ htrc("write done\n");
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Data Base delete line routine for DOS and BLK access methods. */
+/***********************************************************************/
+int DOSFAM::DeleteRecords(PGLOBAL g, int irc)
+ {
+ bool moved;
+ int curpos = ftell(Stream);
+
+ /*********************************************************************/
+ /* There is an alternative here: */
+ /* 1 - use a temporary file in which are copied all not deleted */
+ /* lines, at the end the original file will be deleted and */
+ /* the temporary file renamed to the original file name. */
+ /* 2 - directly move the not deleted lines inside the original */
+ /* file, and at the end erase all trailing records. */
+ /* This will be experimented, but method 1 must be used for Unix as */
+ /* the function needed to erase trailing records is not available. */
+ /*********************************************************************/
+ if (trace)
+ htrc(
+ "DOS DeleteDB: rc=%d UseTemp=%d curpos=%d Fpos=%d Tpos=%d Spos=%d\n",
+ irc, UseTemp, curpos, Fpos, Tpos, Spos);
+
+ if (irc != RC_OK) {
+ /*******************************************************************/
+ /* EOF: position Fpos at the end-of-file position. */
+ /*******************************************************************/
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+
+ if (trace)
+ htrc("Fpos placed at file end=%d\n", Fpos);
+
+ } // endif irc
+
+ if (Tpos == Spos) {
+ /*******************************************************************/
+ /* First line to delete, Open temporary file. */
+ /*******************************************************************/
+ if (UseTemp) {
+ if (OpenTempFile(g))
+ return RC_FX;
+
+ } else {
+ /*****************************************************************/
+ /* Move of eventual preceeding lines is not required here. */
+ /* Set the target file as being the source file itself. */
+ /* Set the future Tpos, and give Spos a value to block copying. */
+ /*****************************************************************/
+ T_Stream = Stream;
+ Spos = Tpos = Fpos;
+ } // endif UseTemp
+
+ } // endif Tpos == Spos
+
+ /*********************************************************************/
+ /* Move any intermediate lines. */
+ /*********************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ if (irc == RC_OK) {
+ /*******************************************************************/
+ /* Reposition the file pointer and set Spos. */
+ /*******************************************************************/
+ if (!UseTemp || moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif
+
+ Spos = GetNextPos(); // New start position
+
+ if (trace)
+ htrc("after: Tpos=%d Spos=%d\n", Tpos, Spos);
+
+ } else {
+ /*******************************************************************/
+ /* Last call after EOF has been reached. */
+ /* The UseTemp case is treated in CloseTableFile. */
+ /*******************************************************************/
+ if (!UseTemp) {
+ /*****************************************************************/
+ /* Because the chsize functionality is only accessible with a */
+ /* system call we must close the file and reopen it with the */
+ /* open function (_fopen for MS ??) this is still to be checked */
+ /* for compatibility with Text files and other OS's. */
+ /*****************************************************************/
+ char filename[_MAX_PATH];
+ int h; // File handle, return code
+
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+ /*rc=*/ PlugCloseFile(g, To_Fb);
+
+ if ((h= global_open(g, MSGID_OPEN_STRERROR, filename, O_WRONLY)) <= 0)
+ return RC_FX;
+
+ /*****************************************************************/
+ /* Remove extra records. */
+ /*****************************************************************/
+#if defined(UNIX)
+ if (ftruncate(h, (off_t)Tpos)) {
+ sprintf(g->Message, MSG(TRUNCATE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#else
+ if (chsize(h, Tpos)) {
+ sprintf(g->Message, MSG(CHSIZE_ERROR), strerror(errno));
+ close(h);
+ return RC_FX;
+ } // endif
+#endif
+
+ close(h);
+
+ if (trace)
+ htrc("done, h=%d irc=%d\n", h, irc);
+
+ } // endif !UseTemp
+
+ } // endif irc
+
+ return RC_OK; // All is correct
+ } // end of DeleteRecords
+
+/***********************************************************************/
+/* Open a temporary file used while updating or deleting. */
+/***********************************************************************/
+bool DOSFAM::OpenTempFile(PGLOBAL g)
+ {
+ char tempname[_MAX_PATH];
+ bool rc = false;
+
+ /*********************************************************************/
+ /* Open the temporary file, Spos is at the beginning of file. */
+ /*********************************************************************/
+ PlugSetPath(tempname, To_File, Tdbp->GetPath());
+ strcat(PlugRemoveType(tempname, tempname), ".t");
+
+ if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ rc = true;
+ } else
+ To_Fbt = PlgGetUser(g)->Openlist;
+
+ return rc;
+ } // end of OpenTempFile
+
+/***********************************************************************/
+/* Move intermediate deleted or updated lines. */
+/* This works only for file open in binary mode. */
+/***********************************************************************/
+bool DOSFAM::MoveIntermediateLines(PGLOBAL g, bool *b)
+ {
+ int n;
+ size_t req, len;
+
+ for (*b = false, n = Fpos - Spos; n > 0; n -= req) {
+ if (!UseTemp || !*b)
+ if (fseek(Stream, Spos, SEEK_SET)) {
+ sprintf(g->Message, MSG(READ_SEEK_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ req = (size_t)min(n, Dbflen);
+ len = fread(DelBuf, 1, req, Stream);
+
+ if (trace)
+ htrc("after read req=%d len=%d\n", req, len);
+
+ if (len != req) {
+ sprintf(g->Message, MSG(DEL_READ_ERROR), (int) req, (int) len);
+ return true;
+ } // endif len
+
+ if (!UseTemp)
+ if (fseek(T_Stream, Tpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(WRITE_SEEK_ERR), strerror(errno));
+ return true;
+ } // endif
+
+ if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
+ sprintf(g->Message, MSG(DEL_WRITE_ERROR), strerror(errno));
+ return true;
+ } // endif
+
+ if (trace)
+ htrc("after write pos=%d\n", ftell(Stream));
+
+ Tpos += (int)req;
+ Spos += (int)req;
+
+ if (trace)
+ htrc("loop: Tpos=%d Spos=%d\n", Tpos, Spos);
+
+ *b = true;
+ } // endfor n
+
+ return false;
+ } // end of MoveIntermediate Lines
+
+/***********************************************************************/
+/* Delete the old file and rename the new temp file. */
+/***********************************************************************/
+int DOSFAM::RenameTempFile(PGLOBAL g)
+ {
+ char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
+ int rc;
+
+ if (!To_Fbt)
+ return RC_INFO; // Nothing to do ???
+
+ // This loop is necessary because, in case of join,
+ // To_File can have been open several times.
+ for (PFBLOCK fb = PlgGetUser(g)->Openlist; fb; fb = fb->Next)
+ rc = PlugCloseFile(g, fb);
+
+ tempname = (char*)To_Fbt->Fname;
+ PlugSetPath(filename, To_File, Tdbp->GetPath());
+ strcat(PlugRemoveType(filetemp, filename), ".ttt");
+ remove(filetemp); // May still be there from previous error
+
+ if (rename(filename, filetemp)) { // Save file for security
+ sprintf(g->Message, MSG(RENAME_ERROR),
+ filename, filetemp, strerror(errno));
+ rc = RC_FX;
+ } else if (rename(tempname, filename)) {
+ sprintf(g->Message, MSG(RENAME_ERROR),
+ tempname, filename, strerror(errno));
+ rc = rename(filetemp, filename); // Restore saved file
+ rc = RC_FX;
+ } else if (remove(filetemp)) {
+ sprintf(g->Message, MSG(REMOVE_ERROR),
+ filetemp, strerror(errno));
+ rc = RC_INFO; // Acceptable
+ } else
+ rc = RC_OK;
+
+ return rc;
+ } // end of RenameTempFile
+
+/***********************************************************************/
+/* Table file close routine for DOS access method. */
+/***********************************************************************/
+void DOSFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc;
+
+ if (UseTemp && T_Stream) {
+ if (Tdbp->Mode == MODE_UPDATE) {
+ // Copy eventually remaining lines
+ bool b;
+
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+ rc = MoveIntermediateLines(g, &b);
+ } // endif Mode
+
+ // Delete the old file and rename the new temp file.
+ RenameTempFile(g); // Also close all files
+ } else {
+ rc = PlugCloseFile(g, To_Fb);
+
+ if (trace)
+ htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
+
+ } // endif UseTemp
+
+ Stream = NULL; // So we can know whether table is open
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for DOS access method. */
+/***********************************************************************/
+void DOSFAM::Rewind(void)
+ {
+ rewind(Stream);
+ Rows = 0;
+ OldBlk = CurBlk = -1;
+ } // end of Rewind
+
+/* --------------------------- Class BLKFAM -------------------------- */
+
+/***********************************************************************/
+/* Constructors. */
+/***********************************************************************/
+BLKFAM::BLKFAM(PDOSDEF tdp) : DOSFAM(tdp)
+ {
+ Blocked = true;
+ Block = tdp->GetBlock();
+ Last = tdp->GetLast();
+ Nrec = tdp->GetElemt();
+ Closing = false;
+ BlkPos = tdp->GetTo_Pos();
+ CurLine = NULL;
+ NxtLine = NULL;
+ OutBuf = NULL;
+ } // end of BLKFAM standard constructor
+
+BLKFAM::BLKFAM(PBLKFAM txfp) : DOSFAM(txfp)
+ {
+ Closing = txfp->Closing;
+ CurLine = txfp->CurLine;
+ NxtLine = txfp->NxtLine;
+ OutBuf = txfp->OutBuf;
+ } // end of BLKFAM copy constructor
+
+/***********************************************************************/
+/* Reset: reset position values at the beginning of file. */
+/***********************************************************************/
+void BLKFAM::Reset(void)
+ {
+ DOSFAM::Reset();
+ Closing = false;
+ } // end of Reset
+
+/***********************************************************************/
+/* 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 BLKFAM::Cardinality(PGLOBAL g)
+ {
+ // Should not be called in this version
+ return (g) ? -1 : 0;
+//return (g) ? (int)((Block - 1) * Nrec + Last) : 1;
+ } // end of Cardinality
+
+/***********************************************************************/
+/* Use BlockTest to reduce the table estimated size. */
+/***********************************************************************/
+int BLKFAM::MaxBlkSize(PGLOBAL g, int s)
+ {
+ int rc = RC_OK, savcur = CurBlk;
+ int size;
+
+ // Roughly estimate the table size as the sum of blocks
+ // that can contain good rows
+ for (size = 0, CurBlk = 0; CurBlk < Block; CurBlk++)
+ if ((rc = Tdbp->TestBlock(g)) == RC_OK)
+ size += (CurBlk == Block - 1) ? Last : Nrec;
+ else if (rc == RC_EF)
+ break;
+
+ CurBlk = savcur;
+ return size;
+ } // end of MaxBlkSize
+
+/***********************************************************************/
+/* Allocate the line buffer. For mode Delete or when a temp file is */
+/* used another big buffer has to be allocated because is it used */
+/* to move or update the lines into the (temp) file. */
+/***********************************************************************/
+bool BLKFAM::AllocateBuffer(PGLOBAL g)
+ {
+ int len;
+ MODE mode = Tdbp->GetMode();
+
+ // For variable length files, Lrecl does not include CRLF
+ len = Lrecl + ((Tdbp->GetFtype()) ? 0 : Ending);
+ Buflen = len * Nrec;
+ CurLine = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
+
+ if (UseTemp || mode == MODE_DELETE) {
+ if (mode == MODE_UPDATE)
+ OutBuf = (char*)PlugSubAlloc(g, NULL, len + 1);
+
+ Dbflen = Buflen;
+ DelBuf = PlugSubAlloc(g, NULL, Dbflen);
+ } else if (mode == MODE_INSERT)
+ Rbuf = Nrec; // To be used by WriteDB
+
+ return false;
+ } // end of AllocateBuffer
+
+/***********************************************************************/
+/* GetRowID: return the RowID of last read record. */
+/***********************************************************************/
+int BLKFAM::GetRowID(void)
+ {
+ return CurNum + Nrec * CurBlk + 1;
+ } // end of GetRowID
+
+/***********************************************************************/
+/* GetPos: return the position of last read record. */
+/***********************************************************************/
+int BLKFAM::GetPos(void)
+ {
+ return (CurNum + Nrec * CurBlk); // Computed file index
+ } // end of GetPos
+
+/***********************************************************************/
+/* GetNextPos: called by DeleteRecords. */
+/***********************************************************************/
+int BLKFAM::GetNextPos(void)
+ {
+ return Fpos + NxtLine - CurLine;
+ } // end of GetNextPos
+
+/***********************************************************************/
+/* SetPos: Replace the table at the specified position. */
+/***********************************************************************/
+bool BLKFAM::SetPos(PGLOBAL g, int pos)
+ {
+ if (pos < 0) {
+ strcpy(g->Message, MSG(INV_REC_POS));
+ return true;
+ } // endif recpos
+
+ CurBlk = pos / Nrec;
+ CurNum = pos % Nrec;
+#if defined(_DEBUG)
+ num_eq[(CurBlk == OldBlk) ? 1 : 0]++;
+#endif
+
+ // Indicate the table position was externally set
+ Placed = true;
+ return false;
+ } // end of SetPos
+
+/***********************************************************************/
+/* Record file position in case of UPDATE or DELETE. */
+/* Not used yet for blocked tables. */
+/***********************************************************************/
+bool BLKFAM::RecordPos(PGLOBAL g)
+ {
+ Fpos = (CurNum + Nrec * CurBlk); // Computed file index
+ return false;
+ } // end of RecordPos
+
+/***********************************************************************/
+/* Skip one record in file. */
+/***********************************************************************/
+int BLKFAM::SkipRecord(PGLOBAL g, bool header)
+ {
+ if (header) {
+ // For Delete
+ Fpos = BlkPos[0]; // First block starts after the header
+
+ if (!UseTemp)
+ Tpos = Spos = Fpos; // No need to move header
+
+ } // endif header
+
+ OldBlk = -2; // To force fseek on first block
+ return RC_OK;
+ } // end of SkipRecord
+
+/***********************************************************************/
+/* ReadBuffer: Read one line for a text file. */
+/***********************************************************************/
+int BLKFAM::ReadBuffer(PGLOBAL g)
+ {
+ int i, n, rc = RC_OK;
+
+ /*********************************************************************/
+ /* Sequential reading when Placed is not true. */
+ /*********************************************************************/
+ if (Placed) {
+ Placed = false;
+ } else if (++CurNum < Rbuf) {
+ CurLine = NxtLine;
+
+ // Get the position of the next line in the buffer
+ while (*NxtLine++ != '\n') ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ goto fin;
+ } else if (Rbuf < Nrec && CurBlk != -1) {
+ return RC_EF;
+ } else {
+ /*******************************************************************/
+ /* New block. */
+ /*******************************************************************/
+ CurNum = 0;
+
+ next:
+ if (++CurBlk >= Block)
+ return RC_EF;
+
+ /*******************************************************************/
+ /* Before reading a new block, check whether block optimization */
+ /* can be done, as well as for join as for local filtering. */
+ /*******************************************************************/
+ switch (Tdbp->TestBlock(g)) {
+ case RC_EF:
+ return RC_EF;
+ case RC_NF:
+ goto next;
+ } // endswitch rc
+
+ } // endif's
+
+ if (OldBlk == CurBlk)
+ goto ok; // Block is already there
+
+ // fseek is required only in non sequential reading
+ if (CurBlk != OldBlk + 1)
+ if (fseek(Stream, BlkPos[CurBlk], SEEK_SET)) {
+ sprintf(g->Message, MSG(FSETPOS_ERROR), BlkPos[CurBlk]);
+ return RC_FX;
+ } // endif fseek
+
+ // Calculate the length of block to read
+ BlkLen = BlkPos[CurBlk + 1] - BlkPos[CurBlk];
+
+ if (trace)
+ htrc("File position is now %d\n", ftell(Stream));
+
+ // Read the entire next block
+ n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
+
+ if (n == BlkLen) {
+// ReadBlks++;
+ num_read++;
+ Rbuf = (CurBlk == Block - 1) ? Last : Nrec;
+
+ ok:
+ rc = RC_OK;
+
+ // Get the position of the current line
+ for (i = 0, CurLine = To_Buf; i < CurNum; i++)
+ while (*CurLine++ != '\n') ; // What about Unix ???
+
+ // Now get the position of the next line
+ for (NxtLine = CurLine; *NxtLine++ != '\n';) ;
+
+ // Set caller line buffer
+ n = NxtLine - CurLine - Ending;
+ memcpy(Tdbp->GetLine(), CurLine, n);
+ Tdbp->GetLine()[n] = '\0';
+ } else if (feof(Stream)) {
+ rc = RC_EF;
+ } else {
+#if defined(UNIX)
+ sprintf(g->Message, MSG(READ_ERROR), To_File, strerror(errno));
+#else
+ sprintf(g->Message, MSG(READ_ERROR), To_File, _strerror(NULL));
+#endif
+
+ if (trace)
+ htrc("%s\n", g->Message);
+
+ return RC_FX;
+ } // endelse
+
+ OldBlk = CurBlk; // Last block actually read
+ IsRead = true; // Is read indeed
+
+ fin:
+ // Store the current record file position for Delete and Update
+ Fpos = BlkPos[CurBlk] + CurLine - To_Buf;
+ return rc;
+ } // end of ReadBuffer
+
+/***********************************************************************/
+/* WriteBuffer: File write routine for the blocked DOS access method. */
+/* Update is directly written back into the file, */
+/* with this (fast) method, record size cannot change. */
+/***********************************************************************/
+int BLKFAM::WriteBuffer(PGLOBAL g)
+ {
+ if (Tdbp->GetMode() == MODE_INSERT) {
+ /*******************************************************************/
+ /* In Insert mode, blocks are added sequentially to the file end. */
+ /*******************************************************************/
+ if (!Closing) { // Add line to the write buffer
+ strcat(strcpy(CurLine, Tdbp->GetLine()), CrLf);
+
+ if (++CurNum != Rbuf) {
+ CurLine += strlen(CurLine);
+ return RC_OK; // We write only full blocks
+ } // endif CurNum
+
+ } // endif Closing
+
+ // Now start the writing process.
+ NxtLine = CurLine + strlen(CurLine);
+ BlkLen = NxtLine - To_Buf;
+
+ if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ Closing = true; // To tell CloseDB about a Write error
+ return RC_FX;
+ } // endif size
+
+ CurBlk++;
+ CurNum = 0;
+ CurLine = To_Buf;
+ } else {
+ /*******************************************************************/
+ /* Mode == MODE_UPDATE. */
+ /*******************************************************************/
+ char *crlf;
+ size_t len;
+ int curpos = ftell(Stream);
+ bool moved = true;
+
+ // T_Stream is the temporary stream or the table file stream itself
+ if (!T_Stream)
+ if (UseTemp /*&& Tdbp->GetMode() == MODE_UPDATE*/) {
+ if (OpenTempFile(g))
+ return RC_FX;
+
+ } else
+ T_Stream = Stream;
+
+ if (UseTemp) {
+ /*****************************************************************/
+ /* We are using a temporary file. Before writing the updated */
+ /* record, we must eventually copy all the intermediate records */
+ /* that have not been updated. */
+ /*****************************************************************/
+ if (MoveIntermediateLines(g, &moved))
+ return RC_FX;
+
+ Spos = GetNextPos(); // New start position
+
+ // Prepare the output buffer
+#if defined(WIN32)
+ crlf = "\r\n";
+#else
+ crlf = "\n";
+#endif // WIN32
+ strcat(strcpy(OutBuf, Tdbp->GetLine()), crlf);
+ len = strlen(OutBuf);
+ } else {
+ if (fseek(Stream, Fpos, SEEK_SET)) { // Fpos is last position
+ sprintf(g->Message, MSG(FSETPOS_ERROR), 0);
+ return RC_FX;
+ } // endif fseek
+
+ // Replace the line inside read buffer (length has not changed)
+ memcpy(CurLine, Tdbp->GetLine(), strlen(Tdbp->GetLine()));
+ OutBuf = CurLine;
+ len = (size_t)(NxtLine - CurLine);
+ } // endif UseTemp
+
+ if (fwrite(OutBuf, 1, len, T_Stream) != (size_t)len) {
+ sprintf(g->Message, MSG(FWRITE_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif fwrite
+
+ if (moved)
+ if (fseek(Stream, curpos, SEEK_SET)) {
+ sprintf(g->Message, MSG(FSEEK_ERROR), strerror(errno));
+ return RC_FX;
+ } // endif
+
+ } // endif Mode
+
+ return RC_OK;
+ } // end of WriteBuffer
+
+/***********************************************************************/
+/* Table file close routine for DOS access method. */
+/***********************************************************************/
+void BLKFAM::CloseTableFile(PGLOBAL g)
+ {
+ int rc, wrc = RC_OK;
+
+ if (UseTemp && T_Stream) {
+ if (Tdbp->GetMode() == MODE_UPDATE) {
+ // Copy eventually remaining lines
+ bool b;
+
+ fseek(Stream, 0, SEEK_END);
+ Fpos = ftell(Stream);
+ rc = MoveIntermediateLines(g, &b);
+ } else
+ rc = RC_OK;
+
+ if (rc == RC_OK)
+ // Delete the old file and rename the new temp file.
+ rc = RenameTempFile(g); // Also close all files
+ else
+ rc = PlugCloseFile(g, To_Fb);
+
+ } else {
+ // Closing is True if last Write was in error
+ if (Tdbp->GetMode() == MODE_INSERT && CurNum && !Closing) {
+ // Some more inserted lines remain to be written
+ Rbuf = CurNum--;
+ Closing = true;
+ wrc = WriteBuffer(g);
+ } else if (Modif && !Closing) {
+ // Last updated block remains to be written
+ Closing = true;
+ wrc = ReadBuffer(g);
+ } // endif's
+
+ rc = PlugCloseFile(g, To_Fb);
+
+ if (trace)
+ htrc("BLK CloseTableFile: closing %s mode=%d wrc=%d rc=%d\n",
+ To_File, Tdbp->GetMode(), wrc, rc);
+
+ } // endif UseTemp
+
+ Stream = NULL; // So we can know whether table is open
+ } // end of CloseTableFile
+
+/***********************************************************************/
+/* Rewind routine for DOS access method. */
+/* Note: commenting out OldBlk = -1 has two advantages: */
+/* 1 - It forces fseek on first block, thus suppressing the need to */
+/* rewind the file, anyway unuseful when second pass if indexed. */
+/* 2 - It permit to avoid re-reading small tables having only 1 block.*/
+/***********************************************************************/
+void BLKFAM::Rewind(void)
+ {
+//rewind(Stream); will be placed by fseek
+ CurBlk = -1;
+ CurNum = Rbuf;
+//OldBlk = -1; commented out in case we reuse last read block
+//Rbuf = 0; commented out in case we reuse last read block
+ } // end of Rewind
+