home *** CD-ROM | disk | FTP | other *** search
/ io Programmo 41 / IOPROG_41.ISO / soft / c++ / CDBFILE.ZIP / cdbfile.cpp next >
Encoding:
C/C++ Source or Header  |  1997-03-10  |  25.1 KB  |  942 lines

  1. /****************************************************************************/
  2. /*********    CDBFile, a dBase III / .DBF file handling object    ***********/
  3. /****************************************************************************/
  4. /*
  5.   
  6.     Copyright (C) 1997 HervΘ GOURMELON, ENSSAT
  7.     gourmelon@enssat.fr
  8.     http://www.enssat.fr
  9.  
  10. This library is free software; you can redistribute it and/or
  11. modify it under the terms of the GNU Library General Public
  12. License as published by the Free Software Foundation; either
  13. version 2 of the License, or (at your option) any later version.
  14.  
  15. This library is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  18. Library General Public License for more details.
  19.  
  20. You should have received a copy of the GNU Library General Public
  21. License along with this library (COPYING2.TXT); if not, write to the
  22. Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
  23. MA 02139, USA.
  24.  
  25. */
  26.  
  27. // If you are building a Windoze MFC app under Visual C++, remove the comment 
  28. // slashes in front of the following line:
  29. //#include "stdafx.h"
  30. // and turn the following lines into comments:
  31.  
  32. #include <string.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <time.h>
  36.  
  37. #include "cdbfile.h" 
  38.  
  39.  
  40. /* Implementation of the member functions for CField        */
  41.  
  42. // Default constructor
  43. CField::CField()
  44. {    Name[0]='\0';
  45.     Type='C';
  46.     Length=0;
  47.     DecCount=0;
  48.     FieldNumber=0;
  49.     Offset=0;
  50.     Next=this;
  51. }
  52.  
  53.  
  54. // Constructor with an initialization of most data members
  55. CField::CField(char* NName,char NType,unsigned char NLength,
  56.                 unsigned char NDecCount,unsigned char FieldNum)
  57. {    
  58.     strcpy(Name, NName);
  59.     Type=NType;
  60.     Length=NLength;
  61.     DecCount=NDecCount;
  62.     FieldNumber=FieldNum;
  63. }
  64.  
  65.  
  66. // Destructor
  67. CField::~CField()
  68. {}
  69.  
  70.  
  71. unsigned short CField::GetNumberOfFields(CField* Start, unsigned short i)
  72. {
  73.     if (Start==NULL) 
  74.         {    Start=this;        // NULL is passed for the initial call of
  75.             i=1;
  76.             return Next->GetNumberOfFields(Start, i);
  77.         }        // this  recursive function in the program 
  78.  
  79.     else 
  80.         if (Start==this) return i;    // We've checked all the elements of    
  81.         else                        //    the dynamic ring: return i */
  82.         {    i+=1;
  83.             return Next->GetNumberOfFields(Start, i); 
  84.             /* Proceed to next element */
  85.         }
  86. }
  87.  
  88.     
  89.     
  90. /* Implementation of the member functions of CDBFile        */
  91.  
  92. /* public : */
  93. // Default constructor
  94. CDBFile::CDBFile()    // Classical init procedure
  95. {
  96.     PathName[0]=0;
  97.     FileHandle=NULL;
  98.     HeaderSize=0;
  99.     FieldCount=0;
  100.     RecordCount=0;
  101.     ModifiedFlag=FALSE;
  102.     FullFileInMemory=FALSE;
  103.     RecordLength=0;
  104.  
  105.     RecordList=NULL;
  106.     FirstField=NULL;
  107. }
  108.  
  109. CDBFile::CDBFile(char* Path)  // Creating the object with a path as argument
  110. // Automatically opens the file
  111. {
  112. if (OpenFile(Path)==FALSE) Path[0]=0;
  113. }
  114.  
  115.  
  116. BOOL CDBFile::Clean()     
  117. // Resets the CDBFile object, regardless of any modifications to be written
  118. {
  119. CField *Delendum;
  120. Record *DelRec;
  121.     
  122.     if (FileHandle!=NULL) 
  123.             fclose(FileHandle); // Closes the file by default
  124.     // Resets all the member data
  125.     PathName[0]=0;
  126.     FileHandle=NULL;
  127.     HeaderSize=0;
  128.     FieldCount=0;
  129.     RecordCount=0;
  130.     ModifiedFlag=FALSE;
  131.     FullFileInMemory=FALSE;
  132.     RecordLength=0;
  133.  
  134.     if (FirstField!=NULL)    // Delete the dynamic list of fields
  135.     {    
  136.         while (FirstField->GetNext()!=FirstField)
  137.         {    Delendum=FirstField->GetNext();
  138.             FirstField->SetNext(Delendum->GetNext());
  139.             delete Delendum;
  140.         }
  141.         delete FirstField;
  142.         FirstField=NULL;
  143.     }
  144.  
  145.     if (RecordList!=NULL)    // Delete the dynamic list of records
  146.     {    
  147.         while (RecordList->Next!=NULL)
  148.         {    DelRec=RecordList->Next;
  149.             RecordList->Next=DelRec->Next;
  150.             delete[] DelRec->Contents;
  151.             delete DelRec;
  152.         }
  153.         delete[] RecordList->Contents; 
  154.         delete RecordList;
  155.         RecordList=NULL;
  156.     }
  157.     return TRUE;
  158.  
  159. }
  160.  
  161. CDBFile::~CDBFile()    //    Default destructor
  162. {
  163.     
  164.     Clean();
  165. }
  166.  
  167.  
  168. void CDBFile::ClearAllRecords()
  169. // Cleaning the contents before rewriting the file (without changing the
  170. // data structure)
  171. {
  172.     Record* DelRec;
  173.  
  174.     if (FileHandle==NULL) return;
  175.     RecordCount=0;
  176.     if (RecordList!=NULL)    // Delete the dynamic list of records
  177.     {    
  178.         while (RecordList->Next!=NULL)
  179.         {    DelRec=RecordList->Next;
  180.             RecordList->Next=DelRec->Next;
  181.             delete[] DelRec->Contents;
  182.             delete DelRec;
  183.         }
  184.         delete[] RecordList->Contents; 
  185.         delete RecordList;
  186.         RecordList=NULL;
  187.     }
  188. }    
  189.  
  190. BOOL CDBFile::OpenFile(char* Path)
  191. {
  192.  
  193. unsigned char dBaseVersion,NLength,NDecCount;
  194. char NType, NName[11];
  195. unsigned short Offst;
  196. CField *Tail, *Current;
  197.  
  198.     // Please refer to the dBase III file structure to debug
  199.     // this procedure
  200.  
  201.     Clean();   // Closes the previous active file and resets the object
  202.  
  203.     if (strlen(Path)>256)
  204.     {    fprintf (stderr, "File path too long : %s \n", Path);
  205.         return FALSE;
  206.     }
  207.     strcpy (PathName, Path);
  208.     FileHandle = fopen (PathName, "r+");
  209.     if (FileHandle==NULL) return FALSE;    // error opening the file
  210.     fseek (FileHandle, 0L, SEEK_SET);
  211.     fread (&dBaseVersion, 1, sizeof (char), FileHandle);
  212.     
  213.     if (dBaseVersion != 3)    // typical of the dBase III files; ignores the 
  214.                             // case of a DBF file with an associated DBT file
  215.     {
  216.         fprintf (stderr, "%s is not a dBASE III file!\n", Path);
  217.         return FALSE;
  218.     }
  219.     
  220.     // Reading the header:
  221.  
  222.     // I ignored the date of last udate. If anybody finds it necessary, just
  223.     // add it to the member data and read it here...
  224.     fseek (FileHandle, 4L, SEEK_SET);
  225.     fread (&RecordCount, sizeof (RecordCount), 1, FileHandle);
  226.  
  227.     fseek (FileHandle, 8L, SEEK_SET);
  228.     fread (&HeaderSize, sizeof (HeaderSize), 1, FileHandle);
  229.     FieldCount = HeaderSize / 32 - 1;
  230.     fread (&RecordLength, sizeof (RecordLength), 1, FileHandle);
  231.  
  232.     Offst=1;    // Data records are preceded by one byte that is 0x20 (space)
  233.     
  234.     // Reading the field descriptor arrays:
  235.  
  236.     for (unsigned char i=1; i<=FieldCount; i++) 
  237.     {
  238.         fseek (FileHandle, (long) 32 * i, SEEK_SET);
  239.         fread (NName, 11, sizeof (char), FileHandle);
  240.         fread (&NType, 1, sizeof (char), FileHandle);
  241.         fseek (FileHandle, (long)(32*i+16), SEEK_SET);
  242.         fread (&NLength, 1, sizeof (unsigned char), FileHandle);
  243.         fread (&NDecCount, 1, sizeof (unsigned char), FileHandle);
  244.         Current=new CField(NName,NType,NLength,NDecCount,i);
  245.         if (FirstField==NULL)    // Creating the list of CFields
  246.         {
  247.             FirstField=Current;
  248.             Current->SetNext(FirstField);
  249.             Tail=FirstField;
  250.         }
  251.         else
  252.         {
  253.             Tail->SetNext(Current);
  254.             Tail=Current;
  255.             Current->SetNext(FirstField);
  256.         }// end if
  257.         Current->SetOffset(Offst);
  258.         Offst+=NLength;
  259.     }// end for
  260.  
  261. return TRUE;
  262. }
  263.  
  264.  
  265. BOOL CDBFile::CloseFile()    // If anybody finds any use to it...  
  266. {                            
  267.     return Clean();
  268. }          
  269.  
  270.         
  271. unsigned long CDBFile::LoadFileToMemory()
  272. // Loads all the records to memory. Necessary when you have to sort them or to
  273. // delete some of them from the file, or to save it as another file.
  274. {
  275.     unsigned long i;
  276.     Record *Current;
  277.  
  278.     // If the file is closed or the records are already loaded, return.
  279.     if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE;
  280.     else
  281.     {
  282.         for (i=1;i<=RecordCount;i++)
  283.         {
  284.             Current=ReadRecord(i);
  285.             Append(Current);    // This will overwrite the records with the
  286.             // same numbers, unless their ModifFlag is set to TRUE
  287.             // (see the Append function)
  288.             if (Current==NULL) return i-1;
  289.         }
  290.          FullFileInMemory=TRUE;
  291.         return i;
  292.     }
  293. }             
  294.  
  295.  
  296. unsigned long CDBFile::WriteAllToFile(char* Path)
  297. // Saves the file as "Path". If "Path"==NULL, the current file is overwritten.
  298. // Returns the number of records actually written.
  299. {
  300.     unsigned long i;
  301.     Record *CurRec;
  302.  
  303.     // If the file is closed or not all the records are loaded, return.
  304.     if ((FileHandle==NULL)||(FullFileInMemory==FALSE)) return 0L;
  305.     else
  306.     {
  307.         if (WriteHeader(Path)==TRUE)    // Writes the header of the file.
  308.         {
  309.             CurRec = RecordList;
  310.             for (i=1;i<=RecordCount;i++)
  311.             {
  312.                 if (WriteRecord(CurRec, i))
  313.                 {
  314.                     if (CurRec->Next==NULL) return i-1;
  315.                     else CurRec=CurRec->Next;
  316.                 }
  317.                 else return i-1;
  318.             }// end for
  319.             return i;
  320.         }//end if WriteHeader
  321.         else return 0L;
  322.     }//end if
  323. }//end
  324.  
  325.  
  326. unsigned long CDBFile::WriteModified()
  327. {
  328.     unsigned long i=0;
  329.     Record* CurRec;
  330.     if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE;
  331.      else
  332.     {    CurRec=RecordList;
  333.         do
  334.         {    if(CurRec->ModifFlag==TRUE)
  335.             {
  336.                 if (WriteRecord(CurRec, CurRec->RecordNumber)) i++;
  337.                 else return i;
  338.             }
  339.             CurRec=CurRec->Next;
  340.         }while(CurRec!=NULL);
  341.         return i;
  342.     }
  343. }
  344.             
  345.  
  346. void CDBFile::SortAllRecords(Record *Head, Record *Tail, 
  347.                                     CField* Criter1 /*, CField* Criter2*/)
  348. // Warning : "Tail" must not be the last record in the list. There must be
  349. // an empty cell following Tail in the list, which should be non-NULL. 
  350. // The function below is an implementation of the quick sort algorithm.
  351. {
  352.  
  353. Record  *i, *j, *s;
  354. void *v1, *vj /*, *v2*/;
  355.  
  356. if ((Head->RecordNumber)<(Tail->RecordNumber))
  357. {
  358.     v1=GetFieldValue(Head, Criter1);
  359.     i=Head;
  360.     j=Head->Next;
  361.     
  362.     s=Tail->Next;
  363.  
  364.     while ((j->RecordNumber)<(s->RecordNumber))
  365.     {
  366.         // Sorting on primary criterium
  367.         vj = GetFieldValue(j,Criter1);
  368.         if (IsSmaller(vj, v1, Criter1))
  369.         {
  370.             Swap (i,j);
  371.             i=i->Next;
  372.             j=j->Next;
  373.         }
  374.         else if (IsBigger(vj, v1, Criter1))
  375.              {
  376.                  Swap(j, s->Previous);
  377.                  s=s->Previous;
  378.              }
  379.     /* attempt to sort on a secundary criterium. Unfortunately, this method failed.
  380.     Any idea ?
  381.          
  382.              else if (Criter2!=NULL) 
  383.                {
  384.                        v2=GetFieldValue(Head, Criter2);     
  385.                     if (IsSmaller(GetFieldValue(j,Criter2), v2, Criter2))
  386.                     {
  387.                         Swap (i,j);
  388.                         i=i->Next;
  389.                         j=j->Next;
  390.                     }
  391.                     else 
  392.                     if (IsBigger(GetFieldValue(j,Criter2), v2, Criter2))
  393.                      {
  394.                          Swap(j, s->Previous);
  395.                          s=s->Previous;
  396.                     }
  397.                     else j=j->Next;         
  398.                }             */
  399.              else j=j->Next;
  400.     DeleteVoidPointer(vj, Criter1);
  401.     DeleteVoidPointer(v1, Criter1);
  402.  
  403.     }
  404.     SortAllRecords(Head, i->Previous, Criter1/*, Criter2*/);
  405.     SortAllRecords(s, Tail, Criter1/*, Criter2*/); 
  406. }//end if
  407. }
  408.  
  409. void CDBFile::SortOn(unsigned short Criterium1/* , unsigned short Criterium2*/)
  410. // Calls SortAllRecords(); the criterium is specified by the field number 
  411. {
  412.     Record *Head, *Tail, *CurRec;
  413.     CField *Crit1 /*, *Crit2*/;    /* Crit2 : optional secundary field */
  414.  
  415.     if (FullFileInMemory!=TRUE) return;
  416.     CurRec=RecordList;
  417.     while (CurRec->Next!=NULL) CurRec=CurRec->Next;
  418.  
  419.     // Two additional records have to be appended temporarily to the list,
  420.     // in order to execute the quick sort correctly.
  421.  
  422.     Tail=new Record;            Head=new Record;
  423.     Head->Next=RecordList;        Tail->Previous=CurRec;
  424.     RecordList->Previous=Head;    CurRec->Next=Tail;
  425.     Head->Previous=NULL;        Tail->Next=NULL;
  426.     Head->Contents=NULL;        Tail->Contents=NULL;
  427.     Head->RecordNumber=0L;        Tail->RecordNumber=RecordCount+1;
  428.  
  429.     Crit1=FirstField->GetField(Criterium1);
  430.     /*Crit2=FirstField->GetField(Criterium2);*/
  431.     SortAllRecords(RecordList, CurRec, Crit1/*, Crit2*/);
  432.  
  433.     RecordList->Previous=NULL;     CurRec->Next=NULL;
  434.     delete Head;                 delete Tail;
  435. }
  436.     
  437.  
  438. void* CDBFile::GetFieldValue(Record* Rec, CField* Field)    
  439. // Returns a pointer to the value of the field *Field in the record *Rec.
  440. // Since we do not know the type of the value, we have to return a "void*"
  441. // pointer, which has to be deleted by the recipient of the pointer.
  442. // See also : SetFieldValue(), DeleteVoidPointer().
  443. {
  444.     char* Data;
  445.     char* RecContents;
  446.  
  447.     RecContents = &(Rec->Contents[Field->GetOffset()]);
  448.     // RecContents points at the beginning of the field within the record.
  449.     Data=new char[Field->GetLength()+1];  // Data is allocated dynamically
  450.     strncpy(Data, RecContents, Field->GetLength());
  451.     // Data now contains the string that has to be converted.
  452.  
  453.     switch(Field->GetType())
  454.     {
  455.         case 'N':  // The field is of numeric type, either :
  456.                 if (Field->GetDecCount()==0)    // an integer (no decimals)
  457.                 {
  458.                     long *Result1;
  459.                     Result1=new long;
  460.                     *Result1=atol(Data);
  461.                     delete []Data;
  462.                     return (void*)Result1;
  463.                 }
  464.                 else                            // a double
  465.                 {
  466.                     double *Result1;    
  467.                     Result1=new double;
  468.                     *Result1=atof(Data);
  469.                     delete []Data;
  470.                     return (void*)Result1;
  471.                 }
  472.         case 'D': // I won't do any specific conversion for dates
  473.                   // Please feel free to do it if you feel like.
  474.         case 'C': // Here, we add a Clipper / FoxPro notion: the field decimal
  475.                   // count is considered as an additional field length number.
  476.                 char* Result2;
  477.                 Result2=strncpy(Data, RecContents,
  478.                              Field->GetLength()+256*Field->GetDecCount()+1);
  479.                 Result2[Field->GetLength()+256*Field->GetDecCount()]=0;
  480.             
  481.                 return (void*)Result2;
  482.         case 'L': // Here I decided that Logical values should be converted to
  483.                   // booleans. I could have kept it as a single character.
  484.                 char c;
  485.                 BOOL* Result3;
  486.                 Result3=new BOOL;
  487.                 sscanf(Data, "%c", c);
  488.                 *Result3=(BOOL)((c=='Y')||(c=='y')||(c=='T')||(c=='t'));
  489.                 delete []Data;
  490.                 return (void*)Result3;
  491.         case 'M':
  492.         default: 
  493.                 return NULL;
  494.     }// end switch;
  495.     
  496. }    
  497.  
  498.  
  499. // Overloaded, public versions of GetFieldValue. :
  500. void* CDBFile::GetFieldValue(char* Field)
  501. {return GetFieldValue(CurrentRec,FirstField->GetField(Field));}
  502.  
  503. void* CDBFile::GetFieldValue(unsigned short FieldNum)
  504. {return GetFieldValue(CurrentRec,FirstField->GetField(FieldNum));}
  505.  
  506.  
  507. void CDBFile::DeleteVoidPointer(void* Pointer, CField* Field)
  508. // This function should be used to delete the void pointers allocated and
  509. // returned by GetFieldValue(). It detects the type of the pointers.
  510. {
  511.     switch (Field->GetType())
  512.     {
  513.     case 'N' :
  514.     case 'L' :
  515.             delete Pointer;
  516.             break;
  517.     case 'D' :
  518.     case 'C' :
  519.     default     :
  520.             delete []Pointer;
  521.     }
  522. }
  523.  
  524.  
  525. // Public overloaded versions of DeleteVoidPointer()
  526. void CDBFile::DeleteVoidPointer(void* Pointer, unsigned short Field)
  527. {DeleteVoidPointer(Pointer,FirstField->GetField(Field));} 
  528.  
  529. void CDBFile::DeleteVoidPointer(void* Pointer, char* Field)
  530. {DeleteVoidPointer(Pointer,FirstField->GetField(Field));}
  531.  
  532.  
  533. Record* CDBFile::GetRecord(unsigned long Number)
  534. // This function returns a record identified by its number. This is for 
  535. // records that are already loaded in memory.
  536. {
  537.     Record* Current;
  538.  
  539.     Current=RecordList;
  540.     if (RecordList==NULL) return NULL;
  541.     else
  542.     {
  543.         if (Current->RecordNumber==Number) return Current;
  544.         else
  545.         {
  546.             while (Current->Next!=NULL)
  547.             {
  548.             Current=Current->Next;
  549.             if (Current->RecordNumber==Number) return Current;
  550.             }
  551.             // The matching record was not found in memory : it
  552.             // must be on the disk... Return the first record.
  553.             return RecordList;        
  554.         }
  555.     }//end "if (RecordList==NULL)"
  556. }
  557.  
  558.  
  559.  
  560. Record* CDBFile::CreateNewRecord()
  561. // When an additional record is created, the number of records in the files is 
  562. // incremented, the file is modified.
  563. {
  564.     Record* Current;
  565.  
  566.     Current=new Record;
  567.     Current->Next=NULL;
  568.     Current->Previous=NULL;
  569.     Current->Contents=new char[RecordLength];
  570.     for (int i=0; i<RecordLength; i++)
  571.         Current->Contents[i]=0x20;
  572.     Current->ModifFlag=FALSE;
  573.     Current->RecordNumber=RecordCount+1;
  574.     RecordCount++;
  575.     ModifiedFlag=TRUE;
  576.     return Current;    
  577. }
  578.  
  579.  
  580. void CDBFile::Append(Record* Rec, Record* Tail)
  581. // Inserts Rec after Tail in the list if Tail!=NULL; inserts it in order
  582. // of RecordNumber if Tail==NULL (which is the default) and if a record with 
  583. // the same number is not already present in the list. In that case, the 
  584. // contents of the former record is replaced by Rec->Contents, unless the 
  585. // former record has been modified and is not saved yet.
  586. {
  587.     Record* Current;
  588.  
  589.     if (Tail==NULL)        // by default...
  590.     {
  591.         Current=RecordList;
  592.         if (RecordList==NULL)  // The list is empty
  593.         {
  594.             RecordList=Rec;       // Create a new list
  595.             return;
  596.         }
  597.         while(Current!=NULL) 
  598.         {
  599.             if (Current->RecordNumber>Rec->RecordNumber)  
  600.             // This is the right place to insert Rec
  601.             {
  602.                 Rec->Next=Current;
  603.                 if (Current->Previous!=NULL) (Current->Previous)->Next=Rec;
  604.                 else RecordList=Rec;
  605.                 Rec->Previous=Current->Previous;
  606.                 Current->Previous=Rec;
  607.                 ModifiedFlag=TRUE;
  608.                 return;
  609.             }
  610.             else if (Current->RecordNumber==Rec->RecordNumber)    
  611.             {
  612.                 if(Current->ModifFlag==TRUE)
  613.                 // Current has been modified, but not saved yet
  614.                 {
  615.                     delete Rec->Contents;
  616.                     delete Rec;
  617.                     return;
  618.                 }
  619.                 else
  620.                 // Current can be replaced by Rec without losing modifications
  621.                 {
  622.                      delete Current->Contents;
  623.                     Current->Contents=Rec->Contents;
  624.                     Current->ModifFlag=Rec->ModifFlag;
  625.                     delete Rec;
  626.                 }
  627.                 // The file has been modified (by adding this record:)
  628.                 ModifiedFlag=TRUE;
  629.                 return;
  630.             }
  631.             else if (Current->Next==NULL)
  632.             // This is the right place to append Rec
  633.             {
  634.                 Rec->Previous=Current;
  635.                 Current->Next=Rec;
  636.                 Rec->Next=NULL;
  637.                 return;
  638.             }//end if
  639.             Current=Current->Next;
  640.         }// end while    
  641.     }// end if
  642.     else 
  643.     {    // Rec is to be inserted after Tail
  644.         Current=Tail;
  645.         Rec->Next=Current->Next;
  646.         Rec->Previous=Current;
  647.         if (Current->Next!=NULL) (Current->Next)->Previous=Rec;
  648.         Current->Next=Rec;
  649.     }
  650. }//end Append
  651.  
  652.     
  653. void CDBFile::DeleteRecord(Record* Rec)
  654. {    // This function really deletes a record if all the records are loaded 
  655.     // in memory and  if the whole data is saved to a file (current file 
  656.     // or a new one). If only a few records are loaded, it will delete the
  657.     // pointed record from the memory, discarding any changes.
  658.     if (Rec==NULL) return;
  659.     if (Rec==RecordList)
  660.     {    RecordList=RecordList->Next;
  661.         if (RecordList) RecordList->Previous=NULL;
  662.         delete(Rec);
  663.         if (FullFileInMemory==TRUE) RecordCount--;
  664.         ModifiedFlag=TRUE;
  665.         return;
  666.     }
  667.     else
  668.     {
  669.         (Rec->Previous)->Next=Rec->Next;
  670.         if (Rec->Next) (Rec->Next)->Previous=Rec->Previous;
  671.         delete(Rec);
  672.         ModifiedFlag=TRUE;
  673.         if (FullFileInMemory==TRUE) RecordCount--;
  674.     }
  675. }
  676.  
  677. void CDBFile::AddField(CField* NewField);    // Not implemented in this version
  678.  
  679. void CDBFile::SetFieldValue(Record* Rec, CField* Field, void* Value)
  680. // Here we have the same problems as in GetFieldValue(), see above. 
  681. {
  682.     char* Data;
  683.     char* RecContents;
  684.     unsigned short FLength;
  685.     unsigned short ResLength;
  686.  
  687.     if (Field->GetType()=='C') 
  688.             FLength=Field->GetLength()+256*Field->GetDecCount();
  689.     else
  690.              FLength=Field->GetLength();
  691.  
  692.     RecContents = &(Rec->Contents[Field->GetOffset()]);
  693.     // RecContents points at the beginning of the field within the record.
  694.     Data=new char[FLength+1]; // Data is allocated dynamically
  695.     for (int i=0; i<FLength; i++) Data[i]=' ';    // Data is blanked
  696.     Data[FLength]=0;  // Last character is set to null, '\0' (end of string)
  697.  
  698.     switch(Field->GetType())
  699.     {
  700.         case 'N':    // Numeric
  701.                 if (Field->GetDecCount()==0)    // Integer (long)
  702.                 {
  703.                     char *Num=new char[FLength+1];
  704.                     long *Result1;
  705.                     Result1=(long *)Value;
  706.                     ltoa(*Result1, Num, 10);
  707.                     ResLength=strlen(Num);
  708.                     // Text alignment for numbers is flush-right :
  709.                     strncpy(&Data[FLength-ResLength], Num, ResLength);
  710.                     strncpy(RecContents, Data, FLength);
  711.                     delete []Num;
  712.                     break;
  713.                 }
  714.                 else                             // Float (double)
  715.                 {
  716.                     char *Num=new char[FLength+1];
  717.                     double *Result1;    
  718.                     Result1=(double *)Value;
  719.                     gcvt(*Result1, FLength,Num);
  720.                     ResLength=strlen(Num);
  721.                     // Text alignment for numbers is flush-right :
  722.                     strncpy(&Data[FLength-ResLength], Num, ResLength);
  723.                     strncpy(RecContents, Data, FLength);
  724.                     delete []Num;
  725.                     break;
  726.                 }
  727.         case 'D':    // Date : no specific conversion (for portability)
  728.         case 'C':    // Character :
  729.                 ResLength=strlen((char *)Value);
  730.                 strncpy(Data, (char *)Value, ResLength);
  731.                 strncpy(RecContents, Data, FLength);
  732.                 break;
  733.         case 'L':     // Logical : conversion from 'BOOL' type
  734.                 BOOL* Result3;
  735.                 Result3=(BOOL *)Value;
  736.                 if (*Result3==TRUE) Data[0]='Y';
  737.                 else Data[0]='N';
  738.                 strncpy(RecContents, Data, FLength);
  739.                 break;
  740.         case 'M':
  741.         default: 
  742.                 break;
  743.     }// end switch;
  744.     delete []Data;
  745.     Rec->ModifFlag=TRUE;
  746. }    
  747.  
  748.  
  749. // Overloaded, public versions of SetFieldValue. :
  750. void CDBFile::SetFieldValue(char* Field, void* Value)
  751. { SetFieldValue(CurrentRec,FirstField->GetField(Field), Value);}
  752.  
  753. void CDBFile::SetFieldValue(unsigned short FieldNum, void* Value)
  754. { SetFieldValue(CurrentRec,FirstField->GetField(FieldNum), Value);}
  755.  
  756.  
  757. void CDBFile::DumpCurrentContents(int i, char* String)
  758. // Dumps the contents of the current record (raw ascii, no formatting)
  759. {
  760.     if (CurrentRec!=NULL)
  761.     {
  762.     strncpy(String, CurrentRec->Contents, i);
  763.     String[i]=0;
  764.     }
  765.     else String[0]=0;
  766. }
  767.  
  768.  
  769.  
  770. Record* CDBFile::ReadRecord(unsigned long RecNum)
  771. // Reads a record from the file, returns a newly allocated *Record pointer
  772. // The file offset is determined by RecNum and RecordLength.
  773. {
  774.     unsigned char Res;
  775.     char* Buffer;
  776.     Record *NewRec;
  777.  
  778.     if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return NULL;
  779.     else
  780.     {
  781.         Buffer=new char[RecordLength];
  782.         fseek (FileHandle, (long)(HeaderSize + (RecNum-1)*RecordLength), SEEK_SET);
  783.         Res=fread (Buffer, RecordLength, 1, FileHandle);
  784.         if (Res==0) return NULL;
  785.         NewRec=new Record;
  786.         NewRec->Contents=Buffer;
  787.         NewRec->ModifFlag=FALSE;
  788.         NewRec->RecordNumber=RecNum;
  789.         NewRec->Next=NULL;
  790.         NewRec->Previous=NULL;
  791.         return NewRec;
  792.     }        
  793. }
  794.  
  795. BOOL CDBFile::WriteHeader(char* Path)
  796. // Use that function to rewrite or to save the current file under another 
  797. // name. It writes the header of the current CDBFile. In order to update the
  798. // "date of last update" field, I introduced two structures : time_t Date, and
  799. // tm *Date_s, which will have to be changed for a port under UNIX. For more
  800. // details, have a look at OpenFile() above and    the dBase III file structure.
  801. {
  802. unsigned char NLength,NDecCount;
  803. char NType;
  804. char Written;
  805. CField *Current;
  806. char dBaseVersion=0x03;
  807. char FieldTerm=0x0D;
  808. time_t Date;
  809. struct tm *Date_s;
  810. char Zero[256];
  811. char Day, Year, Month;
  812.  
  813.     // Initialize a zero buffer;
  814.     for (unsigned short j=0; j<256; j++) Zero[j]=0x00;
  815.  
  816.     // Validity of the path name
  817.     if (Path!=NULL)
  818.     {
  819.         if (strlen(Path)>256)
  820.         {
  821.             fprintf (stderr, "File path too long : %s \n", Path);
  822.             return FALSE;
  823.         }
  824.         strcpy (PathName, Path);
  825.     }
  826.  
  827.     // Close the currently open file
  828.     if (FileHandle!=NULL) fclose(FileHandle);
  829.     FileHandle = fopen (PathName, "w");
  830.     if (FileHandle==NULL) return FALSE;
  831.     fseek (FileHandle, 0L, SEEK_SET);
  832.     Written=fwrite (&dBaseVersion, 1, sizeof (char), FileHandle);
  833.  
  834.  
  835.     time(&Date);
  836.     Date_s = gmtime(&Date);
  837.     Year=(char)(Date_s->tm_year);
  838.     Month=(char)(Date_s->tm_mon);
  839.     Day=(char)(Date_s->tm_mday);
  840.     fseek (FileHandle, 1L, SEEK_SET);
  841.     Written=fwrite (&Year, sizeof (char), 1, FileHandle);
  842.     Written=fwrite (&Month, sizeof (char), 1, FileHandle);
  843.     Written=fwrite (&Day, sizeof (char), 1, FileHandle);
  844.     fseek (FileHandle, 4L, SEEK_SET);
  845.     Written=fwrite (&RecordCount, sizeof (RecordCount), 1, FileHandle);
  846.     fseek (FileHandle, 8L, SEEK_SET);
  847.      Written=fwrite (&HeaderSize, sizeof (HeaderSize), 1, FileHandle);
  848.     Written=fwrite (&RecordLength, sizeof (RecordLength), 1, FileHandle);
  849.     Written=fwrite (&Zero, sizeof(char), 20, FileHandle);
  850.  
  851.     for (unsigned short i=1; i<=FieldCount; i++) 
  852.     {
  853.         
  854.         Current=FirstField->GetField(i);
  855.         NLength=Current->GetLength();
  856.         NType=Current->GetType();
  857.         NDecCount=Current->GetDecCount();
  858.         fwrite (Current->GetName(), 11, sizeof (char), FileHandle);
  859.         fwrite (&NType, sizeof (char), 1, FileHandle);
  860.         fwrite (&Zero, sizeof(char), 4, FileHandle);
  861.         fwrite (&NLength, sizeof (unsigned char), 1, FileHandle);
  862.         fwrite (&NDecCount, sizeof (unsigned char), 1, FileHandle);
  863.         fwrite (&Zero, sizeof(char), 14, FileHandle);
  864.     }// end for
  865.     fwrite (&FieldTerm, sizeof (char), 1, FileHandle);
  866.  
  867. return TRUE;
  868.  
  869. }
  870.  
  871. BOOL CDBFile::WriteRecord(Record* Current, unsigned long RecNum)
  872. // Writes a record at the specified place,using RecNum to calculate the offset
  873. {    
  874.     size_t Res;
  875.  
  876.     if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return FALSE;
  877.     else
  878.     {
  879.         fseek(FileHandle,(long)(HeaderSize+(RecNum-1)*RecordLength),SEEK_SET);
  880.         Res=fwrite (Current->Contents, RecordLength, 1, FileHandle);
  881.         if (Res==1) 
  882.         {
  883.             Current->ModifFlag=FALSE;
  884.             // now that it has been written to file, it's no longer modified.
  885.             return TRUE;
  886.         }
  887.         else return FALSE;
  888.     }        
  889. }    
  890.  
  891. BOOL CDBFile::Swap(Record* Rec1, Record* Rec2)
  892. // swaps the contents of Rec1 and Rec2 (used in the SortAll function)
  893. {
  894.     char* BufCont;
  895.     BOOL BufFlag;
  896.  
  897.  
  898.     BufCont=Rec1->Contents;
  899.     BufFlag=Rec1->ModifFlag;
  900.     Rec1->Contents=Rec2->Contents;
  901.     Rec1->ModifFlag=Rec2->ModifFlag;
  902.     Rec2->Contents=BufCont;
  903.     Rec2->ModifFlag=BufFlag;
  904.     return TRUE;
  905.     
  906. }
  907.  
  908. BOOL CDBFile::IsBigger(void *v1, void *v2, CField* Criterium)
  909. // Compares v1 and v2 using Criterium to determine their type.
  910. {
  911.     switch(Criterium->GetType())
  912.     {
  913.     case 'N':      // Numeric
  914.         double *val1, *val2;
  915.         val1=(double*)v1; val2=(double*)v2;
  916.         return(BOOL)(*val1>*val2);
  917.     case 'C':
  918.     default :      // Caracter
  919.         char *str1, *str2;
  920.         str1=(char*)v1; str2=(char*)v2;
  921.         return(BOOL)(strcmp(str1, str2)>0);
  922.     }
  923. }
  924.  
  925.  
  926. BOOL CDBFile::IsSmaller(void *v1, void *v2, CField* Criterium)
  927. // Compares v1 and v2 using Criterium to determine their type.
  928. {
  929.     switch(Criterium->GetType())
  930.     {
  931.     case 'N':        // Numeric
  932.         double *val1, *val2;
  933.         val1=(double*)v1; val2=(double*)v2;
  934.         return(BOOL)(*val1<*val2);
  935.     case 'C':
  936.     default :        // Caracter
  937.         char *str1, *str2;
  938.         str1=(char*)v1; str2=(char*)v2;
  939.         return(BOOL)(strcmp(str2, str1)>0);
  940.     }
  941. }
  942.