mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2123 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2123 lines
		
	
	
	
		
			70 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*********** File AM Txt C++ Program Source Code File (.CPP) ***********/
 | 
						|
/* PROGRAM NAME: FILAMTXT                                              */
 | 
						|
/* -------------                                                       */
 | 
						|
/*  Version 1.8                                                        */
 | 
						|
/*                                                                     */
 | 
						|
/* COPYRIGHT:                                                          */
 | 
						|
/* ----------                                                          */
 | 
						|
/*  (C) Copyright to the author Olivier BERTRAND          2005-2020    */
 | 
						|
/*                                                                     */
 | 
						|
/* 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 <m_string.h>
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  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"
 | 
						|
#include "tabjson.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
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Routine called externally by TXTFAM SortedRows functions.          */
 | 
						|
/***********************************************************************/
 | 
						|
PARRAY MakeValueArray(PGLOBAL g, PPARM pp);
 | 
						|
 | 
						|
/* --------------------------- Class TXTFAM -------------------------- */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Constructors.                                                      */
 | 
						|
/***********************************************************************/
 | 
						|
TXTFAM::TXTFAM(PDOSDEF tdp)
 | 
						|
  {
 | 
						|
  Tdbp = NULL;
 | 
						|
  To_Fb = NULL;
 | 
						|
 | 
						|
	if (tdp) {
 | 
						|
		To_File = tdp->Fn;
 | 
						|
		Lrecl = tdp->Lrecl;
 | 
						|
		Eof = tdp->Eof;
 | 
						|
		Ending = tdp->Ending;
 | 
						|
	} else {
 | 
						|
		To_File = NULL;
 | 
						|
		Lrecl = 0;
 | 
						|
		Eof = false;
 | 
						|
#if defined(_WIN32)
 | 
						|
		Ending = 2;
 | 
						|
#else
 | 
						|
		Ending = 1;
 | 
						|
#endif
 | 
						|
	}	// endif tdp
 | 
						|
 | 
						|
  Placed = false;
 | 
						|
  IsRead = true;
 | 
						|
  Blocked = false;
 | 
						|
  To_Buf = NULL;
 | 
						|
  DelBuf = NULL;
 | 
						|
  BlkPos = NULL;
 | 
						|
  To_Pos = NULL;
 | 
						|
  To_Sos = NULL;
 | 
						|
  To_Upd = NULL;
 | 
						|
  Posar = NULL;
 | 
						|
  Sosar = NULL;
 | 
						|
  Updar = 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;
 | 
						|
  Fpos = Spos = Tpos = 0;
 | 
						|
  Padded = false;
 | 
						|
  Abort = false;
 | 
						|
  CrLf = (char*)(Ending == 1 ? "\n" : "\r\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;
 | 
						|
  To_Pos = txfp->To_Pos;
 | 
						|
  To_Sos = txfp->To_Sos;
 | 
						|
  To_Upd = txfp->To_Upd;
 | 
						|
  Posar = txfp->Posar;
 | 
						|
  Sosar = txfp->Sosar;
 | 
						|
  Updar = txfp->Updar;
 | 
						|
  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;
 | 
						|
  Fpos = txfp->Fpos;
 | 
						|
  Spos = txfp->Spos;
 | 
						|
  Tpos = txfp->Tpos;
 | 
						|
  Padded = txfp->Padded;
 | 
						|
  Eof = txfp->Eof;
 | 
						|
  Ending = txfp->Ending;
 | 
						|
  Abort = txfp->Abort;
 | 
						|
  CrLf = txfp->CrLf;
 | 
						|
  } // 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(1))
 | 
						|
    htrc("GetFileLength: fn=%s h=%d\n", filename, h);
 | 
						|
 | 
						|
  if (h == -1) {
 | 
						|
    if (errno != ENOENT) {
 | 
						|
      if (trace(1))
 | 
						|
        htrc("%s\n", g->Message);
 | 
						|
 | 
						|
      len = -1;
 | 
						|
    } else {
 | 
						|
      len = 0;          // File does not exist yet
 | 
						|
      g->Message[0]= '\0';
 | 
						|
    } // endif errno
 | 
						|
 | 
						|
  } else {
 | 
						|
    if ((len = _filelength(h)) < 0)
 | 
						|
      snprintf(g->Message, sizeof(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
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
 | 
						|
 | 
						|
      } else {
 | 
						|
        if (!(len % Lrecl))
 | 
						|
          card = len / (int)Lrecl;           // Fixed length file
 | 
						|
        else
 | 
						|
          snprintf(g->Message, sizeof(g->Message), MSG(NOT_FIXED_LEN), To_File, len, Lrecl);
 | 
						|
 | 
						|
      } // endif Padded
 | 
						|
 | 
						|
      if (trace(1))
 | 
						|
        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
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  AddListValue: Used when doing indexed update or delete.            */
 | 
						|
/***********************************************************************/
 | 
						|
bool TXTFAM::AddListValue(PGLOBAL g, int type, void *val, PPARM *top)
 | 
						|
  {
 | 
						|
  PPARM pp = (PPARM)PlugSubAlloc(g, NULL, sizeof(PARM));
 | 
						|
 | 
						|
  switch (type) {
 | 
						|
//  case TYPE_INT:
 | 
						|
//    pp->Value = PlugSubAlloc(g, NULL, sizeof(int));
 | 
						|
//    *((int*)pp->Value) = *((int*)val);
 | 
						|
//    break;
 | 
						|
    case TYPE_VOID:
 | 
						|
      pp->Intval = *(int*)val;
 | 
						|
      break;
 | 
						|
//  case TYPE_STRING:
 | 
						|
//    pp->Value = PlugDup(g, (char*)val);
 | 
						|
//    break;
 | 
						|
    case TYPE_PCHAR:
 | 
						|
      pp->Value = val;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return true;
 | 
						|
  } // endswitch type
 | 
						|
 | 
						|
  pp->Type = type;
 | 
						|
  pp->Domain = 0;
 | 
						|
  pp->Next = *top;
 | 
						|
  *top = pp;
 | 
						|
  return false;
 | 
						|
  } // end of AddListValue
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Store needed values for indexed UPDATE or DELETE.                  */
 | 
						|
/***********************************************************************/
 | 
						|
int TXTFAM::StoreValues(PGLOBAL g, bool upd)
 | 
						|
{
 | 
						|
  int  pos = GetPos();
 | 
						|
  bool rc = AddListValue(g, TYPE_VOID, &pos, &To_Pos);
 | 
						|
 | 
						|
  if (!rc) {
 | 
						|
    pos = GetNextPos();
 | 
						|
    rc = AddListValue(g, TYPE_VOID, &pos, &To_Sos);
 | 
						|
    } // endif rc
 | 
						|
 | 
						|
  if (upd && !rc) {
 | 
						|
    char *buf;
 | 
						|
 | 
						|
    if (Tdbp->PrepareWriting(g))
 | 
						|
      return RC_FX;
 | 
						|
 | 
						|
    buf = PlugDup(g, Tdbp->GetLine());
 | 
						|
    rc = AddListValue(g, TYPE_PCHAR, buf, &To_Upd);
 | 
						|
    } // endif upd
 | 
						|
 | 
						|
  return rc ? RC_FX : RC_OK;
 | 
						|
} // end of StoreValues
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  UpdateSortedRows. When updating using indexing, the issue is that  */
 | 
						|
/*  record are not necessarily updated in sequential order.            */
 | 
						|
/*  Moving intermediate lines cannot be done while making them because */
 | 
						|
/*  this can cause extra wrong records to be included in the new file. */
 | 
						|
/*  What we do here is reorder the updated records and do all the      */
 | 
						|
/*  updates ordered by record position.                                */
 | 
						|
/***********************************************************************/
 | 
						|
int TXTFAM::UpdateSortedRows(PGLOBAL g)
 | 
						|
  {
 | 
						|
  int  *ix, i;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Get the stored update values and sort them.                      */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (!(Posar = MakeValueArray(g, To_Pos))) {
 | 
						|
//  strcpy(g->Message, "Position array is null");
 | 
						|
//  return RC_INFO;
 | 
						|
    return RC_OK;         // Nothing to do
 | 
						|
  } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), "Start position array is null");
 | 
						|
    goto err;
 | 
						|
  } else if (!(Updar = MakeValueArray(g, To_Upd))) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), "Updated line array is null");
 | 
						|
    goto err;
 | 
						|
  } else if (!(ix = (int*)Posar->GetSortIndex(g))) { 
 | 
						|
    snprintf(g->Message, sizeof(g->Message), "Error getting array sort index");
 | 
						|
    goto err;
 | 
						|
  } // endif's
 | 
						|
 | 
						|
  Rewind();
 | 
						|
 | 
						|
  for (i = 0; i < Posar->GetNval(); i++) {
 | 
						|
    SetPos(g, Sosar->GetIntValue(ix[i]));
 | 
						|
    Fpos = Posar->GetIntValue(ix[i]);
 | 
						|
    strcpy(Tdbp->To_Line, Updar->GetStringValue(ix[i]));
 | 
						|
 | 
						|
    // Now write the updated line.
 | 
						|
    if (WriteBuffer(g))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    } // endfor i
 | 
						|
 | 
						|
  return RC_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  if (trace(1))
 | 
						|
    htrc("%s\n", g->Message);
 | 
						|
 | 
						|
  return RC_FX;
 | 
						|
  } // end of UpdateSortedRows
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  DeleteSortedRows. When deleting using indexing, the issue is that  */
 | 
						|
/*  record are not necessarily deleted in sequential order. Moving     */
 | 
						|
/*  intermediate lines cannot be done while deleting them because      */
 | 
						|
/*  this can cause extra wrong records to be included in the new file. */
 | 
						|
/*  What we do here is to reorder the deleted record and delete from   */
 | 
						|
/*  the file from the ordered deleted records.                         */
 | 
						|
/***********************************************************************/
 | 
						|
int TXTFAM::DeleteSortedRows(PGLOBAL g)
 | 
						|
  {
 | 
						|
  int  *ix, i, irc;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Get the stored delete values and sort them.                      */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (!(Posar = MakeValueArray(g, To_Pos))) {
 | 
						|
//  strcpy(g->Message, "Position array is null");
 | 
						|
//  return RC_INFO;
 | 
						|
    return RC_OK;             // Nothing to do
 | 
						|
  } else if (!(Sosar = MakeValueArray(g, To_Sos))) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), "Start position array is null");
 | 
						|
    goto err;
 | 
						|
  } else if (!(ix = (int*)Posar->GetSortIndex(g))) { 
 | 
						|
    snprintf(g->Message, sizeof(g->Message), "Error getting array sort index");
 | 
						|
    goto err;
 | 
						|
  } // endif's
 | 
						|
 | 
						|
  Tpos = Spos = 0;
 | 
						|
 | 
						|
  for (i = 0; i < Posar->GetNval(); i++) {
 | 
						|
    if ((irc = InitDelete(g, Posar->GetIntValue(ix[i]), 
 | 
						|
                             Sosar->GetIntValue(ix[i]))) == RC_FX)
 | 
						|
      goto err;
 | 
						|
 | 
						|
    // Now delete the sorted rows
 | 
						|
    if (DeleteRecords(g, irc))
 | 
						|
      goto err;
 | 
						|
 | 
						|
    } // endfor i
 | 
						|
 | 
						|
  return RC_OK;
 | 
						|
 | 
						|
err:
 | 
						|
  if (trace(1))
 | 
						|
    htrc("%s\n", g->Message);
 | 
						|
 | 
						|
  return RC_FX;
 | 
						|
  } // end of DeleteSortedRows
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  The purpose of this function is to deal with access methods that   */
 | 
						|
/*  are not coherent regarding the use of SetPos and GetPos.           */
 | 
						|
/***********************************************************************/
 | 
						|
int TXTFAM::InitDelete(PGLOBAL g, int, int)
 | 
						|
  {
 | 
						|
  snprintf(g->Message, sizeof(g->Message), "InitDelete should not be used by this table type");
 | 
						|
  return RC_FX;
 | 
						|
  } // end of InitDelete
 | 
						|
 | 
						|
/* --------------------------- Class DOSFAM -------------------------- */
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Constructors.                                                      */
 | 
						|
/***********************************************************************/
 | 
						|
DOSFAM::DOSFAM(PDOSDEF tdp) : TXTFAM(tdp)
 | 
						|
  {
 | 
						|
  To_Fbt = NULL;
 | 
						|
  Stream = NULL;
 | 
						|
  T_Stream = NULL;
 | 
						|
  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;
 | 
						|
  UseTemp = tdfp->UseTemp;
 | 
						|
  Bin = tdfp->Bin;
 | 
						|
  } // end of DOSFAM copy constructor
 | 
						|
 | 
						|
DOSFAM::DOSFAM(PBLKFAM tdfp, PDOSDEF tdp) : TXTFAM(tdp)
 | 
						|
  {
 | 
						|
  Tdbp = tdfp->Tdbp;
 | 
						|
  To_Fb = tdfp->To_Fb;
 | 
						|
  To_Fbt = tdfp->To_Fbt;
 | 
						|
  Stream = tdfp->Stream;
 | 
						|
  T_Stream = tdfp->T_Stream;
 | 
						|
  UseTemp = tdfp->UseTemp;
 | 
						|
  Bin = tdfp->Bin;
 | 
						|
  } // end of DOSFAM constructor from BLKFAM
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  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)
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", To_File);
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    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, 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 and vice versa
 | 
						|
//Bin = (Blocked || Ending != CRLF);
 | 
						|
  Bin = true;             // To avoid ftell problems
 | 
						|
 | 
						|
  switch (mode) {
 | 
						|
    case MODE_READ:
 | 
						|
      snprintf(opmode, sizeof(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
 | 
						|
        snprintf(opmode, sizeof(opmode), "w");
 | 
						|
        Tdbp->ResetSize();
 | 
						|
        break;
 | 
						|
        } // endif
 | 
						|
 | 
						|
      // Selective delete, pass thru
 | 
						|
      Bin = true;
 | 
						|
      /* fall through */
 | 
						|
    case MODE_UPDATE:
 | 
						|
      if ((UseTemp = Tdbp->IsUsingTemp(g))) {
 | 
						|
        snprintf(opmode, sizeof(opmode), "r");
 | 
						|
        Bin = true;
 | 
						|
      } else
 | 
						|
        snprintf(opmode, sizeof(opmode), "r+");
 | 
						|
 | 
						|
      break;
 | 
						|
    case MODE_INSERT:
 | 
						|
      snprintf(opmode, sizeof(opmode), "a+");
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | 
						|
      return true;
 | 
						|
    } // endswitch Mode
 | 
						|
 | 
						|
  // For blocked I/O or for moving lines, open the table in binary
 | 
						|
  safe_strcat(opmode, sizeof(opmode), (Bin) ? "b" : "t");
 | 
						|
 | 
						|
  // Now open the file stream
 | 
						|
  PlugSetPath(filename, To_File, Tdbp->GetPath());
 | 
						|
 | 
						|
  if (!(Stream = PlugOpenFile(g, filename, opmode))) {
 | 
						|
    if (trace(1))
 | 
						|
      htrc("%s\n", g->Message);
 | 
						|
 | 
						|
    return (mode == MODE_READ && errno == ENOENT)
 | 
						|
            ? PushWarning(g, Tdbp) : true;
 | 
						|
    } // endif Stream
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    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) + 1;     // Sergei
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    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)) {
 | 
						|
    snprintf(g->Message, sizeof(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) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(FTELL_ERROR), 0, strerror(errno));
 | 
						|
//  strcat(g->Message, " (possible wrong ENDING option value)");
 | 
						|
    return true;
 | 
						|
    } // endif Fpos
 | 
						|
 | 
						|
  return false;
 | 
						|
  } // end of RecordPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Initialize Fpos and the current position for indexed DELETE.       */
 | 
						|
/***********************************************************************/
 | 
						|
int DOSFAM::InitDelete(PGLOBAL g, int fpos, int spos)
 | 
						|
  {
 | 
						|
  Fpos = fpos;
 | 
						|
 | 
						|
  if (fseek(Stream, spos, SEEK_SET)) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), Fpos);
 | 
						|
    return RC_FX;
 | 
						|
    } // endif
 | 
						|
 | 
						|
  return RC_OK;
 | 
						|
  } // end of InitDelete
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  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(_WIN32)
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
 | 
						|
#else
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(0));
 | 
						|
#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(2))
 | 
						|
    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(2))
 | 
						|
      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(2))
 | 
						|
    htrc(" About to read: stream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
 | 
						|
                          Stream, To_Buf, Buflen, Fpos);
 | 
						|
 | 
						|
  if (fgets(To_Buf, Buflen, Stream)) {
 | 
						|
    p = To_Buf + strlen(To_Buf) - 1;
 | 
						|
 | 
						|
    if (trace(2))
 | 
						|
      htrc(" Read: To_Buf=%p p=%c\n", To_Buf, p);
 | 
						|
 | 
						|
#if defined(_WIN32)
 | 
						|
    if (Bin) {
 | 
						|
      // Data file is read in binary so CRLF remains
 | 
						|
#else
 | 
						|
    if (true) {
 | 
						|
      // Data files can be imported from Windows (having CRLF)
 | 
						|
#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(2))
 | 
						|
      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(_WIN32)
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
 | 
						|
#else
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(0));
 | 
						|
#endif
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("%s\n", g->Message);
 | 
						|
 | 
						|
    rc = RC_FX;
 | 
						|
  } // endif's fgets
 | 
						|
 | 
						|
  if (trace(2))
 | 
						|
    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)
 | 
						|
  {
 | 
						|
  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;
 | 
						|
 | 
						|
    } // endif T_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(1))
 | 
						|
      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)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), 0);
 | 
						|
        return RC_FX;
 | 
						|
        } // endif
 | 
						|
 | 
						|
    } // endif mode
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Prepare the write the updated line.                              */
 | 
						|
  /*********************************************************************/
 | 
						|
  strcat(strcpy(To_Buf, Tdbp->To_Line), (Bin) ? CrLf : "\n");
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  Now start the writing process.                                   */
 | 
						|
  /*********************************************************************/
 | 
						|
  if ((fputs(To_Buf, T_Stream)) == EOF) {
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(FPUTS_ERROR), strerror(errno));
 | 
						|
    return RC_FX;
 | 
						|
    } // endif EOF
 | 
						|
 | 
						|
  if (Tdbp->Mode == MODE_UPDATE && moved)
 | 
						|
    if (fseek(Stream, curpos, SEEK_SET)) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | 
						|
      return RC_FX;
 | 
						|
      } // endif
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    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.                                       */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (trace(1))
 | 
						|
    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(1))
 | 
						|
      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 preceding 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)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), 0);
 | 
						|
        return RC_FX;
 | 
						|
        } // endif
 | 
						|
 | 
						|
    Spos = GetNextPos();                     // New start position
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      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 & !Abort) {
 | 
						|
      /*****************************************************************/
 | 
						|
      /*  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(_WIN32)
 | 
						|
      if (chsize(h, Tpos)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(CHSIZE_ERROR), strerror(errno));
 | 
						|
        close(h);
 | 
						|
        return RC_FX;
 | 
						|
        } // endif
 | 
						|
#else
 | 
						|
      if (ftruncate(h, (off_t)Tpos)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | 
						|
        close(h);
 | 
						|
        return RC_FX;
 | 
						|
        } // endif
 | 
						|
#endif
 | 
						|
 | 
						|
      close(h);
 | 
						|
 | 
						|
      if (trace(1))
 | 
						|
        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());
 | 
						|
  PlugRemoveType(tempname, tempname);
 | 
						|
  safe_strcat(tempname, sizeof(tempname), ".t");
 | 
						|
 | 
						|
  if (!(T_Stream = PlugOpenFile(g, tempname, "wb"))) {
 | 
						|
    if (trace(1))
 | 
						|
      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)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(READ_SEEK_ERROR), strerror(errno));
 | 
						|
        return true;
 | 
						|
        } // endif
 | 
						|
 | 
						|
    req = (size_t)MY_MIN(n, Dbflen);
 | 
						|
    len = fread(DelBuf, 1, req, Stream);
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("after read req=%d len=%d\n", req, len);
 | 
						|
 | 
						|
    if (len != req) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(DEL_READ_ERROR), (int) req, (int) len);
 | 
						|
      return true;
 | 
						|
      } // endif len
 | 
						|
 | 
						|
    if (!UseTemp)
 | 
						|
      if (fseek(T_Stream, Tpos, SEEK_SET)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(WRITE_SEEK_ERR), strerror(errno));
 | 
						|
        return true;
 | 
						|
        } // endif
 | 
						|
 | 
						|
    if ((len = fwrite(DelBuf, 1, req, T_Stream)) != req) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(DEL_WRITE_ERROR), strerror(errno));
 | 
						|
      return true;
 | 
						|
      } // endif
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("after write pos=%d\n", ftell(Stream));
 | 
						|
 | 
						|
    Tpos += (int)req;
 | 
						|
    Spos += (int)req;
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      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.                  */
 | 
						|
/*  If aborting just delete the new temp file.                         */
 | 
						|
/*  If indexed, make the temp file from the arrays.                    */
 | 
						|
/***********************************************************************/
 | 
						|
int DOSFAM::RenameTempFile(PGLOBAL g)
 | 
						|
  {
 | 
						|
  char *tempname, filetemp[_MAX_PATH], filename[_MAX_PATH];
 | 
						|
  int   rc = RC_OK;
 | 
						|
 | 
						|
  if (To_Fbt)
 | 
						|
    tempname = (char*)To_Fbt->Fname;
 | 
						|
  else
 | 
						|
    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)
 | 
						|
    if (fb == To_Fb || (fb == To_Fbt))
 | 
						|
      rc = PlugCloseFile(g, fb);
 | 
						|
  
 | 
						|
  if (!Abort) {
 | 
						|
    PlugSetPath(filename, To_File, Tdbp->GetPath());
 | 
						|
    PlugRemoveType(filetemp, filename);
 | 
						|
    safe_strcat(filetemp, sizeof(filetemp), ".ttt");
 | 
						|
    remove(filetemp);   // May still be there from previous error
 | 
						|
 | 
						|
    if (rename(filename, filetemp)) {    // Save file for security
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(RENAME_ERROR),
 | 
						|
              filename, filetemp, strerror(errno));
 | 
						|
			throw 51;
 | 
						|
		} else if (rename(tempname, filename)) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(RENAME_ERROR),
 | 
						|
              tempname, filename, strerror(errno));
 | 
						|
      rc = rename(filetemp, filename);   // Restore saved file
 | 
						|
			throw 52;
 | 
						|
		} else if (remove(filetemp)) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(REMOVE_ERROR),
 | 
						|
              filetemp, strerror(errno));
 | 
						|
      rc = RC_INFO;                      // Acceptable
 | 
						|
    } // endif's
 | 
						|
 | 
						|
  } else
 | 
						|
    remove(tempname);
 | 
						|
 | 
						|
  return rc;
 | 
						|
  } // end of RenameTempFile
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Table file close routine for DOS access method.                    */
 | 
						|
/***********************************************************************/
 | 
						|
void DOSFAM::CloseTableFile(PGLOBAL g, bool abort)
 | 
						|
  {
 | 
						|
  int rc;
 | 
						|
 | 
						|
  Abort = abort;
 | 
						|
 | 
						|
  if (UseTemp && T_Stream) {
 | 
						|
    if (Tdbp->Mode == MODE_UPDATE && !Abort) {
 | 
						|
      // Copy eventually remaining lines
 | 
						|
      bool b;
 | 
						|
 | 
						|
      fseek(Stream, 0, SEEK_END);
 | 
						|
      Fpos = ftell(Stream);
 | 
						|
      Abort = MoveIntermediateLines(g, &b) != RC_OK;
 | 
						|
      } // endif Abort
 | 
						|
 | 
						|
    // Delete the old file and rename the new temp file.
 | 
						|
    rc = RenameTempFile(g);     // Also close all files
 | 
						|
  } else {
 | 
						|
    rc = PlugCloseFile(g, To_Fb);
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      htrc("DOS Close: closing %s rc=%d\n", To_File, rc);
 | 
						|
 | 
						|
  } // endif UseTemp
 | 
						|
 | 
						|
  Stream = NULL;           // So we can know whether table is open
 | 
						|
  T_Stream = NULL;
 | 
						|
  } // end of CloseTableFile
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Rewind routine for DOS access method.                              */
 | 
						|
/***********************************************************************/
 | 
						|
void DOSFAM::Rewind(void)
 | 
						|
  {
 | 
						|
  if (Stream)  // Can be NULL when making index on void table
 | 
						|
    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)
 | 
						|
  {
 | 
						|
  return (g) ? ((Block > 0) ? (int)((Block - 1) * Nrec + Last) : 0) : 1;
 | 
						|
  } // end of Cardinality
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Use BlockTest to reduce the table estimated size.                  */
 | 
						|
/***********************************************************************/
 | 
						|
int BLKFAM::MaxBlkSize(PGLOBAL g, int)
 | 
						|
  {
 | 
						|
  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 (int)(Fpos + NxtLine - CurLine);
 | 
						|
  } // end of GetNextPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  SetPos: Replace the table at the specified position.               */
 | 
						|
/***********************************************************************/
 | 
						|
bool BLKFAM::SetPos(PGLOBAL g, int)
 | 
						|
  {
 | 
						|
  snprintf(g->Message, sizeof(g->Message), "Blocked variable tables cannot be used indexed");
 | 
						|
  return true;
 | 
						|
  } // end of SetPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Record file position in case of UPDATE or DELETE.                  */
 | 
						|
/*  Not used yet for blocked tables.                                   */
 | 
						|
/***********************************************************************/
 | 
						|
bool BLKFAM::RecordPos(PGLOBAL)
 | 
						|
  {
 | 
						|
  Fpos = (CurNum + Nrec * CurBlk);          // Computed file index
 | 
						|
  return false;
 | 
						|
  } // end of RecordPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Skip one record in file.                                           */
 | 
						|
/***********************************************************************/
 | 
						|
int BLKFAM::SkipRecord(PGLOBAL, 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, rc = RC_OK;
 | 
						|
  size_t n;
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
  /*  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)) {
 | 
						|
      snprintf(g->Message, sizeof(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(1))
 | 
						|
    htrc("File position is now %d\n", ftell(Stream));
 | 
						|
 | 
						|
  // Read the entire next block
 | 
						|
  n = fread(To_Buf, 1, (size_t)BlkLen, Stream);
 | 
						|
 | 
						|
  if ((size_t) n == (size_t) 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(_WIN32)
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
 | 
						|
#else
 | 
						|
    snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(errno));
 | 
						|
#endif
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      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 = (int)(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 = (int)(NxtLine - To_Buf);
 | 
						|
 | 
						|
    if (fwrite(To_Buf, 1, BlkLen, Stream) != (size_t)BlkLen) {
 | 
						|
      snprintf(g->Message, sizeof(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.                                           */
 | 
						|
    /*******************************************************************/
 | 
						|
    const 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
 | 
						|
        snprintf(g->Message, sizeof(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) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(FWRITE_ERROR), strerror(errno));
 | 
						|
      return RC_FX;
 | 
						|
      } // endif fwrite
 | 
						|
 | 
						|
    if (moved)
 | 
						|
      if (fseek(Stream, curpos, SEEK_SET)) {
 | 
						|
        snprintf(g->Message, sizeof(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, bool abort)
 | 
						|
  {
 | 
						|
  int rc, wrc = RC_OK;
 | 
						|
 | 
						|
  Abort = abort;
 | 
						|
 | 
						|
  if (UseTemp && T_Stream) {
 | 
						|
    if (Tdbp->GetMode() == MODE_UPDATE && !Abort) {
 | 
						|
      // Copy eventually remaining lines
 | 
						|
      bool b;
 | 
						|
 | 
						|
      fseek(Stream, 0, SEEK_END);
 | 
						|
      Fpos = ftell(Stream);
 | 
						|
      Abort = MoveIntermediateLines(g, &b) != RC_OK;
 | 
						|
      } // endif Abort
 | 
						|
 | 
						|
    // Delete the old file and rename the new temp file.
 | 
						|
    rc = RenameTempFile(g);    // Also close all files
 | 
						|
  } 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(1))
 | 
						|
      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
 | 
						|
 | 
						|
/* --------------------------- Class BINFAM -------------------------- */
 | 
						|
 | 
						|
#if 0
 | 
						|
/***********************************************************************/
 | 
						|
/*  BIN GetFileLength: returns file size in number of bytes.           */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::GetFileLength(PGLOBAL g)
 | 
						|
{
 | 
						|
	int len;
 | 
						|
 | 
						|
	if (!Stream)
 | 
						|
		len = TXTFAM::GetFileLength(g);
 | 
						|
	else
 | 
						|
		if ((len = _filelength(_fileno(Stream))) < 0)
 | 
						|
			snprintf(g->Message, sizeof(g->Message), MSG(FILELEN_ERROR), "_filelength", To_File);
 | 
						|
 | 
						|
	xtrc(1, "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 BINFAM::Cardinality(PGLOBAL g)
 | 
						|
{
 | 
						|
	return (g) ? -1 : 0;
 | 
						|
} // end of Cardinality
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  OpenTableFile: Open a DOS/UNIX table file using C standard I/Os.   */
 | 
						|
/***********************************************************************/
 | 
						|
bool BINFAM::OpenTableFile(PGLOBAL g) {
 | 
						|
	char    opmode[4], filename[_MAX_PATH];
 | 
						|
	MODE    mode = Tdbp->GetMode();
 | 
						|
	PDBUSER dbuserp = PlgGetUser(g);
 | 
						|
 | 
						|
	switch (mode) {
 | 
						|
	case MODE_READ:
 | 
						|
		snprintf(opmode, sizeof(opmode), "rb");
 | 
						|
		break;
 | 
						|
	case MODE_WRITE:
 | 
						|
		snprintf(opmode, sizeof(opmode), "wb");
 | 
						|
		break;
 | 
						|
	default:
 | 
						|
		snprintf(g->Message, sizeof(g->Message), MSG(BAD_OPEN_MODE), mode);
 | 
						|
		return true;
 | 
						|
	} // endswitch Mode
 | 
						|
 | 
						|
	// Now open the file stream
 | 
						|
	PlugSetPath(filename, To_File, Tdbp->GetPath());
 | 
						|
 | 
						|
	if (!(Stream = PlugOpenFile(g, filename, opmode))) {
 | 
						|
		if (trace(1))
 | 
						|
			htrc("%s\n", g->Message);
 | 
						|
 | 
						|
		return (mode == MODE_READ && errno == ENOENT)
 | 
						|
			? PushWarning(g, Tdbp) : true;
 | 
						|
	} // endif Stream
 | 
						|
 | 
						|
	if (trace(1))
 | 
						|
		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.                                        */
 | 
						|
	/*********************************************************************/
 | 
						|
	return AllocateBuffer(g);
 | 
						|
} // end of OpenTableFile
 | 
						|
#endif // 0
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  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 BINFAM::AllocateBuffer(PGLOBAL g)
 | 
						|
{
 | 
						|
  MODE mode = Tdbp->GetMode();
 | 
						|
 | 
						|
  // Lrecl is Ok
 | 
						|
  Buflen = Lrecl;
 | 
						|
 | 
						|
  // Buffer will be allocated separately
 | 
						|
  if (mode == MODE_ANY) {
 | 
						|
    xtrc(1, "SubAllocating a buffer of %d bytes\n", Buflen);
 | 
						|
    To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
 | 
						|
  } else if (UseTemp || mode == MODE_DELETE) {
 | 
						|
    // Have a big buffer to move lines
 | 
						|
    Dbflen = Buflen * DOS_BUFF_LEN;
 | 
						|
    DelBuf = PlugSubAlloc(g, NULL, Dbflen);
 | 
						|
  } // endif mode
 | 
						|
 | 
						|
  return false;
 | 
						|
#if 0
 | 
						|
  MODE mode = Tdbp->GetMode();
 | 
						|
 | 
						|
  // Lrecl is Ok
 | 
						|
  Dbflen = Buflen = Lrecl;
 | 
						|
 | 
						|
  if (trace(1))
 | 
						|
    htrc("SubAllocating a buffer of %d bytes\n", Buflen);
 | 
						|
 | 
						|
  DelBuf = To_Buf = (char*)PlugSubAlloc(g, NULL, Buflen);
 | 
						|
  return false;
 | 
						|
#endif // 0
 | 
						|
} // end of AllocateBuffer
 | 
						|
 | 
						|
#if 0
 | 
						|
/***********************************************************************/
 | 
						|
/*  GetRowID: return the RowID of last read record.                    */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::GetRowID(void) {
 | 
						|
	return Rows;
 | 
						|
} // end of GetRowID
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  GetPos: return the position of last read record.                   */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::GetPos(void) {
 | 
						|
	return Fpos;
 | 
						|
} // end of GetPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  GetNextPos: return the position of next record.                    */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::GetNextPos(void) {
 | 
						|
	return ftell(Stream);
 | 
						|
} // end of GetNextPos
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  SetPos: Replace the table at the specified position.               */
 | 
						|
/***********************************************************************/
 | 
						|
bool BINFAM::SetPos(PGLOBAL g, int pos) {
 | 
						|
	Fpos = pos;
 | 
						|
 | 
						|
	if (fseek(Stream, Fpos, SEEK_SET)) {
 | 
						|
		snprintf(g->Message, sizeof(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 BINFAM::RecordPos(PGLOBAL g) {
 | 
						|
	if ((Fpos = ftell(Stream)) < 0) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), MSG(FTELL_ERROR), 0, strerror(errno));
 | 
						|
		//  strcat(g->Message, " (possible wrong ENDING option value)");
 | 
						|
		return true;
 | 
						|
	} // endif Fpos
 | 
						|
 | 
						|
	return false;
 | 
						|
} // end of RecordPos
 | 
						|
#endif // 0
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  ReadBuffer: Read one line for a text file.                         */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::ReadBuffer(PGLOBAL g)
 | 
						|
{
 | 
						|
	int rc;
 | 
						|
 | 
						|
	if (!Stream)
 | 
						|
		return RC_EF;
 | 
						|
 | 
						|
	xtrc(2, "ReadBuffer: Tdbp=%p To_Line=%p Placed=%d\n",
 | 
						|
					Tdbp, Tdbp->GetLine(), Placed);
 | 
						|
 | 
						|
	if (!Placed) {
 | 
						|
		/*******************************************************************/
 | 
						|
		/*  Record file position in case of UPDATE or DELETE.              */
 | 
						|
		/*******************************************************************/
 | 
						|
		if (RecordPos(g))
 | 
						|
			return RC_FX;
 | 
						|
 | 
						|
		CurBlk = (int)Rows++;
 | 
						|
		xtrc(2, "ReadBuffer: CurBlk=%d\n", CurBlk);
 | 
						|
	} else
 | 
						|
		Placed = false;
 | 
						|
 | 
						|
	xtrc(2, " About to read: bstream=%p To_Buf=%p Buflen=%d Fpos=%d\n",
 | 
						|
						Stream, To_Buf, Buflen, Fpos);
 | 
						|
 | 
						|
	// Read the prefix giving the row length
 | 
						|
	if (!fread(&Recsize, sizeof(size_t), 1, Stream)) {
 | 
						|
		if (!feof(Stream)) {
 | 
						|
			snprintf(g->Message, sizeof(g->Message), "Error reading line prefix\n");
 | 
						|
			return RC_FX;
 | 
						|
		} else
 | 
						|
			return RC_EF;
 | 
						|
 | 
						|
	} else if (Recsize > (unsigned)Buflen) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), "Record too big (Recsize=%zd Buflen=%d)\n", Recsize, Buflen);
 | 
						|
		return RC_FX;
 | 
						|
	}	// endif Recsize
 | 
						|
 | 
						|
	if (fread(To_Buf, Recsize, 1, Stream)) {
 | 
						|
		xtrc(2, " Read: To_Buf=%p Recsize=%zd\n", To_Buf, Recsize);
 | 
						|
		num_read++;
 | 
						|
		rc = RC_OK;
 | 
						|
	} else if (feof(Stream)) {
 | 
						|
		rc = RC_EF;
 | 
						|
	} else {
 | 
						|
#if defined(_WIN32)
 | 
						|
		snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, _strerror(NULL));
 | 
						|
#else
 | 
						|
		snprintf(g->Message, sizeof(g->Message), MSG(READ_ERROR), To_File, strerror(0));
 | 
						|
#endif
 | 
						|
		xtrc(2, "%s\n", g->Message);
 | 
						|
		rc = RC_FX;
 | 
						|
	} // endif's fread
 | 
						|
 | 
						|
	xtrc(2, "ReadBuffer: rc=%d\n", rc);
 | 
						|
	IsRead = true;
 | 
						|
	return rc;
 | 
						|
} // end of ReadBuffer
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  WriteBuffer: File write routine for BIN access method.             */
 | 
						|
/***********************************************************************/
 | 
						|
int BINFAM::WriteBuffer(PGLOBAL g)
 | 
						|
{
 | 
						|
	int   curpos = 0;
 | 
						|
	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;
 | 
						|
 | 
						|
  } // endif T_Stream
 | 
						|
 | 
						|
  if (Tdbp->GetMode() == 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(1))
 | 
						|
      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)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), 0);
 | 
						|
        return RC_FX;
 | 
						|
      } // endif
 | 
						|
 | 
						|
  } // endif mode
 | 
						|
 | 
						|
  /*********************************************************************/
 | 
						|
	/*  Prepare writing the line.                                        */
 | 
						|
	/*********************************************************************/
 | 
						|
//memcpy(To_Buf, Tdbp->GetLine(), Recsize);
 | 
						|
 | 
						|
	/*********************************************************************/
 | 
						|
	/*  Now start the writing process.                                   */
 | 
						|
	/*********************************************************************/
 | 
						|
	if (fwrite(&Recsize, sizeof(size_t), 1, T_Stream) != 1) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), "Error %d writing prefix to %s",
 | 
						|
			errno, To_File);
 | 
						|
		return RC_FX;
 | 
						|
	} else if (fwrite(To_Buf, Recsize, 1, T_Stream) != 1) {
 | 
						|
		snprintf(g->Message, sizeof(g->Message), "Error %d writing %zd bytes to %s",
 | 
						|
			errno, Recsize, To_File);
 | 
						|
		return RC_FX;
 | 
						|
	} // endif fwrite
 | 
						|
 | 
						|
  if (Tdbp->GetMode() == MODE_UPDATE && moved)
 | 
						|
    if (fseek(Stream, curpos, SEEK_SET)) {
 | 
						|
      snprintf(g->Message, sizeof(g->Message), MSG(FSEEK_ERROR), strerror(errno));
 | 
						|
      return RC_FX;
 | 
						|
    } // endif
 | 
						|
 | 
						|
  xtrc(1, "Binary write done\n");
 | 
						|
	return RC_OK;
 | 
						|
} // end of WriteBuffer
 | 
						|
 | 
						|
#if 0
 | 
						|
/***********************************************************************/
 | 
						|
/*  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.                                       */
 | 
						|
  /*********************************************************************/
 | 
						|
  if (trace(1))
 | 
						|
    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(1))
 | 
						|
      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 preceding 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)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(FSETPOS_ERROR), 0);
 | 
						|
        return RC_FX;
 | 
						|
      } // endif
 | 
						|
 | 
						|
    Spos = GetNextPos();                     // New start position
 | 
						|
 | 
						|
    if (trace(1))
 | 
						|
      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 & !Abort) {
 | 
						|
      /*****************************************************************/
 | 
						|
      /*  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(_WIN32)
 | 
						|
      if (chsize(h, Tpos)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(CHSIZE_ERROR), strerror(errno));
 | 
						|
        close(h);
 | 
						|
        return RC_FX;
 | 
						|
      } // endif
 | 
						|
#else
 | 
						|
      if (ftruncate(h, (off_t)Tpos)) {
 | 
						|
        snprintf(g->Message, sizeof(g->Message), MSG(TRUNCATE_ERROR), strerror(errno));
 | 
						|
        close(h);
 | 
						|
        return RC_FX;
 | 
						|
      } // endif
 | 
						|
#endif
 | 
						|
 | 
						|
      close(h);
 | 
						|
 | 
						|
      if (trace(1))
 | 
						|
        htrc("done, h=%d irc=%d\n", h, irc);
 | 
						|
 | 
						|
    } // endif !UseTemp
 | 
						|
 | 
						|
  } // endif irc
 | 
						|
 | 
						|
  return RC_OK;                                      // All is correct
 | 
						|
} // end of DeleteRecords
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Table file close routine for DOS access method.                    */
 | 
						|
/***********************************************************************/
 | 
						|
void BINFAM::CloseTableFile(PGLOBAL g, bool abort)
 | 
						|
{
 | 
						|
  int rc;
 | 
						|
 | 
						|
  Abort = abort;
 | 
						|
  rc = PlugCloseFile(g, To_Fb);
 | 
						|
  xtrc(1, "BIN Close: closing %s rc=%d\n", To_File, rc);
 | 
						|
  Stream = NULL;           // So we can know whether table is open
 | 
						|
} // end of CloseTableFile
 | 
						|
 | 
						|
/***********************************************************************/
 | 
						|
/*  Rewind routine for BIN access method.                              */
 | 
						|
/***********************************************************************/
 | 
						|
void BINFAM::Rewind(void)
 | 
						|
{
 | 
						|
	if (Stream)  // Can be NULL when making index on void table
 | 
						|
		rewind(Stream);
 | 
						|
 | 
						|
	Rows = 0;
 | 
						|
	OldBlk = CurBlk = -1;
 | 
						|
} // end of Rewind
 | 
						|
#endif // 0
 |