home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************/
- /********* CDBFile, a dBase III / .DBF file handling object ***********/
- /****************************************************************************/
- /*
-
- Copyright (C) 1997 HervĪ GOURMELON, ENSSAT
- gourmelon@enssat.fr
- http://www.enssat.fr
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library (COPYING2.TXT); if not, write to the
- Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
- MA 02139, USA.
-
- */
-
- // If you are building a Windoze MFC app under Visual C++, remove the comment
- // slashes in front of the following line:
- //#include "stdafx.h"
- // and turn the following lines into comments:
-
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
-
- #include "cdbfile.h"
-
-
- /* Implementation of the member functions for CField */
-
- // Default constructor
- CField::CField()
- { Name[0]='\0';
- Type='C';
- Length=0;
- DecCount=0;
- FieldNumber=0;
- Offset=0;
- Next=this;
- }
-
-
- // Constructor with an initialization of most data members
- CField::CField(char* NName,char NType,unsigned char NLength,
- unsigned char NDecCount,unsigned char FieldNum)
- {
- strcpy(Name, NName);
- Type=NType;
- Length=NLength;
- DecCount=NDecCount;
- FieldNumber=FieldNum;
- }
-
-
- // Destructor
- CField::~CField()
- {}
-
-
- unsigned short CField::GetNumberOfFields(CField* Start, unsigned short i)
- {
- if (Start==NULL)
- { Start=this; // NULL is passed for the initial call of
- i=1;
- return Next->GetNumberOfFields(Start, i);
- } // this recursive function in the program
-
- else
- if (Start==this) return i; // We've checked all the elements of
- else // the dynamic ring: return i */
- { i+=1;
- return Next->GetNumberOfFields(Start, i);
- /* Proceed to next element */
- }
- }
-
-
-
- /* Implementation of the member functions of CDBFile */
-
- /* public : */
- // Default constructor
- CDBFile::CDBFile() // Classical init procedure
- {
- PathName[0]=0;
- FileHandle=NULL;
- HeaderSize=0;
- FieldCount=0;
- RecordCount=0;
- ModifiedFlag=FALSE;
- FullFileInMemory=FALSE;
- RecordLength=0;
-
- RecordList=NULL;
- FirstField=NULL;
- }
-
- CDBFile::CDBFile(char* Path) // Creating the object with a path as argument
- // Automatically opens the file
- {
- if (OpenFile(Path)==FALSE) Path[0]=0;
- }
-
-
- BOOL CDBFile::Clean()
- // Resets the CDBFile object, regardless of any modifications to be written
- {
- CField *Delendum;
- Record *DelRec;
-
- if (FileHandle!=NULL)
- fclose(FileHandle); // Closes the file by default
- // Resets all the member data
- PathName[0]=0;
- FileHandle=NULL;
- HeaderSize=0;
- FieldCount=0;
- RecordCount=0;
- ModifiedFlag=FALSE;
- FullFileInMemory=FALSE;
- RecordLength=0;
-
- if (FirstField!=NULL) // Delete the dynamic list of fields
- {
- while (FirstField->GetNext()!=FirstField)
- { Delendum=FirstField->GetNext();
- FirstField->SetNext(Delendum->GetNext());
- delete Delendum;
- }
- delete FirstField;
- FirstField=NULL;
- }
-
- if (RecordList!=NULL) // Delete the dynamic list of records
- {
- while (RecordList->Next!=NULL)
- { DelRec=RecordList->Next;
- RecordList->Next=DelRec->Next;
- delete[] DelRec->Contents;
- delete DelRec;
- }
- delete[] RecordList->Contents;
- delete RecordList;
- RecordList=NULL;
- }
- return TRUE;
-
- }
-
- CDBFile::~CDBFile() // Default destructor
- {
-
- Clean();
- }
-
-
- void CDBFile::ClearAllRecords()
- // Cleaning the contents before rewriting the file (without changing the
- // data structure)
- {
- Record* DelRec;
-
- if (FileHandle==NULL) return;
- RecordCount=0;
- if (RecordList!=NULL) // Delete the dynamic list of records
- {
- while (RecordList->Next!=NULL)
- { DelRec=RecordList->Next;
- RecordList->Next=DelRec->Next;
- delete[] DelRec->Contents;
- delete DelRec;
- }
- delete[] RecordList->Contents;
- delete RecordList;
- RecordList=NULL;
- }
- }
-
- BOOL CDBFile::OpenFile(char* Path)
- {
-
- unsigned char dBaseVersion,NLength,NDecCount;
- char NType, NName[11];
- unsigned short Offst;
- CField *Tail, *Current;
-
- // Please refer to the dBase III file structure to debug
- // this procedure
-
- Clean(); // Closes the previous active file and resets the object
-
- if (strlen(Path)>256)
- { fprintf (stderr, "File path too long : %s \n", Path);
- return FALSE;
- }
- strcpy (PathName, Path);
- FileHandle = fopen (PathName, "r+");
- if (FileHandle==NULL) return FALSE; // error opening the file
- fseek (FileHandle, 0L, SEEK_SET);
- fread (&dBaseVersion, 1, sizeof (char), FileHandle);
-
- if (dBaseVersion != 3) // typical of the dBase III files; ignores the
- // case of a DBF file with an associated DBT file
- {
- fprintf (stderr, "%s is not a dBASE III file!\n", Path);
- return FALSE;
- }
-
- // Reading the header:
-
- // I ignored the date of last udate. If anybody finds it necessary, just
- // add it to the member data and read it here...
- fseek (FileHandle, 4L, SEEK_SET);
- fread (&RecordCount, sizeof (RecordCount), 1, FileHandle);
-
- fseek (FileHandle, 8L, SEEK_SET);
- fread (&HeaderSize, sizeof (HeaderSize), 1, FileHandle);
- FieldCount = HeaderSize / 32 - 1;
- fread (&RecordLength, sizeof (RecordLength), 1, FileHandle);
-
- Offst=1; // Data records are preceded by one byte that is 0x20 (space)
-
- // Reading the field descriptor arrays:
-
- for (unsigned char i=1; i<=FieldCount; i++)
- {
- fseek (FileHandle, (long) 32 * i, SEEK_SET);
- fread (NName, 11, sizeof (char), FileHandle);
- fread (&NType, 1, sizeof (char), FileHandle);
- fseek (FileHandle, (long)(32*i+16), SEEK_SET);
- fread (&NLength, 1, sizeof (unsigned char), FileHandle);
- fread (&NDecCount, 1, sizeof (unsigned char), FileHandle);
- Current=new CField(NName,NType,NLength,NDecCount,i);
- if (FirstField==NULL) // Creating the list of CFields
- {
- FirstField=Current;
- Current->SetNext(FirstField);
- Tail=FirstField;
- }
- else
- {
- Tail->SetNext(Current);
- Tail=Current;
- Current->SetNext(FirstField);
- }// end if
- Current->SetOffset(Offst);
- Offst+=NLength;
- }// end for
-
- return TRUE;
- }
-
-
- BOOL CDBFile::CloseFile() // If anybody finds any use to it...
- {
- return Clean();
- }
-
-
- unsigned long CDBFile::LoadFileToMemory()
- // Loads all the records to memory. Necessary when you have to sort them or to
- // delete some of them from the file, or to save it as another file.
- {
- unsigned long i;
- Record *Current;
-
- // If the file is closed or the records are already loaded, return.
- if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE;
- else
- {
- for (i=1;i<=RecordCount;i++)
- {
- Current=ReadRecord(i);
- Append(Current); // This will overwrite the records with the
- // same numbers, unless their ModifFlag is set to TRUE
- // (see the Append function)
- if (Current==NULL) return i-1;
- }
- FullFileInMemory=TRUE;
- return i;
- }
- }
-
-
- unsigned long CDBFile::WriteAllToFile(char* Path)
- // Saves the file as "Path". If "Path"==NULL, the current file is overwritten.
- // Returns the number of records actually written.
- {
- unsigned long i;
- Record *CurRec;
-
- // If the file is closed or not all the records are loaded, return.
- if ((FileHandle==NULL)||(FullFileInMemory==FALSE)) return 0L;
- else
- {
- if (WriteHeader(Path)==TRUE) // Writes the header of the file.
- {
- CurRec = RecordList;
- for (i=1;i<=RecordCount;i++)
- {
- if (WriteRecord(CurRec, i))
- {
- if (CurRec->Next==NULL) return i-1;
- else CurRec=CurRec->Next;
- }
- else return i-1;
- }// end for
- return i;
- }//end if WriteHeader
- else return 0L;
- }//end if
- }//end
-
-
- unsigned long CDBFile::WriteModified()
- {
- unsigned long i=0;
- Record* CurRec;
- if ((FileHandle==NULL)||(FullFileInMemory==TRUE)) return FALSE;
- else
- { CurRec=RecordList;
- do
- { if(CurRec->ModifFlag==TRUE)
- {
- if (WriteRecord(CurRec, CurRec->RecordNumber)) i++;
- else return i;
- }
- CurRec=CurRec->Next;
- }while(CurRec!=NULL);
- return i;
- }
- }
-
-
- void CDBFile::SortAllRecords(Record *Head, Record *Tail,
- CField* Criter1 /*, CField* Criter2*/)
- // Warning : "Tail" must not be the last record in the list. There must be
- // an empty cell following Tail in the list, which should be non-NULL.
- // The function below is an implementation of the quick sort algorithm.
- {
-
- Record *i, *j, *s;
- void *v1, *vj /*, *v2*/;
-
- if ((Head->RecordNumber)<(Tail->RecordNumber))
- {
- v1=GetFieldValue(Head, Criter1);
- i=Head;
- j=Head->Next;
-
- s=Tail->Next;
-
- while ((j->RecordNumber)<(s->RecordNumber))
- {
- // Sorting on primary criterium
- vj = GetFieldValue(j,Criter1);
- if (IsSmaller(vj, v1, Criter1))
- {
- Swap (i,j);
- i=i->Next;
- j=j->Next;
- }
- else if (IsBigger(vj, v1, Criter1))
- {
- Swap(j, s->Previous);
- s=s->Previous;
- }
- /* attempt to sort on a secundary criterium. Unfortunately, this method failed.
- Any idea ?
-
- else if (Criter2!=NULL)
- {
- v2=GetFieldValue(Head, Criter2);
- if (IsSmaller(GetFieldValue(j,Criter2), v2, Criter2))
- {
- Swap (i,j);
- i=i->Next;
- j=j->Next;
- }
- else
- if (IsBigger(GetFieldValue(j,Criter2), v2, Criter2))
- {
- Swap(j, s->Previous);
- s=s->Previous;
- }
- else j=j->Next;
- } */
- else j=j->Next;
- DeleteVoidPointer(vj, Criter1);
- DeleteVoidPointer(v1, Criter1);
-
- }
- SortAllRecords(Head, i->Previous, Criter1/*, Criter2*/);
- SortAllRecords(s, Tail, Criter1/*, Criter2*/);
- }//end if
- }
-
- void CDBFile::SortOn(unsigned short Criterium1/* , unsigned short Criterium2*/)
- // Calls SortAllRecords(); the criterium is specified by the field number
- {
- Record *Head, *Tail, *CurRec;
- CField *Crit1 /*, *Crit2*/; /* Crit2 : optional secundary field */
-
- if (FullFileInMemory!=TRUE) return;
- CurRec=RecordList;
- while (CurRec->Next!=NULL) CurRec=CurRec->Next;
-
- // Two additional records have to be appended temporarily to the list,
- // in order to execute the quick sort correctly.
-
- Tail=new Record; Head=new Record;
- Head->Next=RecordList; Tail->Previous=CurRec;
- RecordList->Previous=Head; CurRec->Next=Tail;
- Head->Previous=NULL; Tail->Next=NULL;
- Head->Contents=NULL; Tail->Contents=NULL;
- Head->RecordNumber=0L; Tail->RecordNumber=RecordCount+1;
-
- Crit1=FirstField->GetField(Criterium1);
- /*Crit2=FirstField->GetField(Criterium2);*/
- SortAllRecords(RecordList, CurRec, Crit1/*, Crit2*/);
-
- RecordList->Previous=NULL; CurRec->Next=NULL;
- delete Head; delete Tail;
- }
-
-
- void* CDBFile::GetFieldValue(Record* Rec, CField* Field)
- // Returns a pointer to the value of the field *Field in the record *Rec.
- // Since we do not know the type of the value, we have to return a "void*"
- // pointer, which has to be deleted by the recipient of the pointer.
- // See also : SetFieldValue(), DeleteVoidPointer().
- {
- char* Data;
- char* RecContents;
-
- RecContents = &(Rec->Contents[Field->GetOffset()]);
- // RecContents points at the beginning of the field within the record.
- Data=new char[Field->GetLength()+1]; // Data is allocated dynamically
- strncpy(Data, RecContents, Field->GetLength());
- // Data now contains the string that has to be converted.
-
- switch(Field->GetType())
- {
- case 'N': // The field is of numeric type, either :
- if (Field->GetDecCount()==0) // an integer (no decimals)
- {
- long *Result1;
- Result1=new long;
- *Result1=atol(Data);
- delete []Data;
- return (void*)Result1;
- }
- else // a double
- {
- double *Result1;
- Result1=new double;
- *Result1=atof(Data);
- delete []Data;
- return (void*)Result1;
- }
- case 'D': // I won't do any specific conversion for dates
- // Please feel free to do it if you feel like.
- case 'C': // Here, we add a Clipper / FoxPro notion: the field decimal
- // count is considered as an additional field length number.
- char* Result2;
- Result2=strncpy(Data, RecContents,
- Field->GetLength()+256*Field->GetDecCount()+1);
- Result2[Field->GetLength()+256*Field->GetDecCount()]=0;
-
- return (void*)Result2;
- case 'L': // Here I decided that Logical values should be converted to
- // booleans. I could have kept it as a single character.
- char c;
- BOOL* Result3;
- Result3=new BOOL;
- sscanf(Data, "%c", c);
- *Result3=(BOOL)((c=='Y')||(c=='y')||(c=='T')||(c=='t'));
- delete []Data;
- return (void*)Result3;
- case 'M':
- default:
- return NULL;
- }// end switch;
-
- }
-
-
- // Overloaded, public versions of GetFieldValue. :
- void* CDBFile::GetFieldValue(char* Field)
- {return GetFieldValue(CurrentRec,FirstField->GetField(Field));}
-
- void* CDBFile::GetFieldValue(unsigned short FieldNum)
- {return GetFieldValue(CurrentRec,FirstField->GetField(FieldNum));}
-
-
- void CDBFile::DeleteVoidPointer(void* Pointer, CField* Field)
- // This function should be used to delete the void pointers allocated and
- // returned by GetFieldValue(). It detects the type of the pointers.
- {
- switch (Field->GetType())
- {
- case 'N' :
- case 'L' :
- delete Pointer;
- break;
- case 'D' :
- case 'C' :
- default :
- delete []Pointer;
- }
- }
-
-
- // Public overloaded versions of DeleteVoidPointer()
- void CDBFile::DeleteVoidPointer(void* Pointer, unsigned short Field)
- {DeleteVoidPointer(Pointer,FirstField->GetField(Field));}
-
- void CDBFile::DeleteVoidPointer(void* Pointer, char* Field)
- {DeleteVoidPointer(Pointer,FirstField->GetField(Field));}
-
-
- Record* CDBFile::GetRecord(unsigned long Number)
- // This function returns a record identified by its number. This is for
- // records that are already loaded in memory.
- {
- Record* Current;
-
- Current=RecordList;
- if (RecordList==NULL) return NULL;
- else
- {
- if (Current->RecordNumber==Number) return Current;
- else
- {
- while (Current->Next!=NULL)
- {
- Current=Current->Next;
- if (Current->RecordNumber==Number) return Current;
- }
- // The matching record was not found in memory : it
- // must be on the disk... Return the first record.
- return RecordList;
- }
- }//end "if (RecordList==NULL)"
- }
-
-
-
- Record* CDBFile::CreateNewRecord()
- // When an additional record is created, the number of records in the files is
- // incremented, the file is modified.
- {
- Record* Current;
-
- Current=new Record;
- Current->Next=NULL;
- Current->Previous=NULL;
- Current->Contents=new char[RecordLength];
- for (int i=0; i<RecordLength; i++)
- Current->Contents[i]=0x20;
- Current->ModifFlag=FALSE;
- Current->RecordNumber=RecordCount+1;
- RecordCount++;
- ModifiedFlag=TRUE;
- return Current;
- }
-
-
- void CDBFile::Append(Record* Rec, Record* Tail)
- // Inserts Rec after Tail in the list if Tail!=NULL; inserts it in order
- // of RecordNumber if Tail==NULL (which is the default) and if a record with
- // the same number is not already present in the list. In that case, the
- // contents of the former record is replaced by Rec->Contents, unless the
- // former record has been modified and is not saved yet.
- {
- Record* Current;
-
- if (Tail==NULL) // by default...
- {
- Current=RecordList;
- if (RecordList==NULL) // The list is empty
- {
- RecordList=Rec; // Create a new list
- return;
- }
- while(Current!=NULL)
- {
- if (Current->RecordNumber>Rec->RecordNumber)
- // This is the right place to insert Rec
- {
- Rec->Next=Current;
- if (Current->Previous!=NULL) (Current->Previous)->Next=Rec;
- else RecordList=Rec;
- Rec->Previous=Current->Previous;
- Current->Previous=Rec;
- ModifiedFlag=TRUE;
- return;
- }
- else if (Current->RecordNumber==Rec->RecordNumber)
- {
- if(Current->ModifFlag==TRUE)
- // Current has been modified, but not saved yet
- {
- delete Rec->Contents;
- delete Rec;
- return;
- }
- else
- // Current can be replaced by Rec without losing modifications
- {
- delete Current->Contents;
- Current->Contents=Rec->Contents;
- Current->ModifFlag=Rec->ModifFlag;
- delete Rec;
- }
- // The file has been modified (by adding this record:)
- ModifiedFlag=TRUE;
- return;
- }
- else if (Current->Next==NULL)
- // This is the right place to append Rec
- {
- Rec->Previous=Current;
- Current->Next=Rec;
- Rec->Next=NULL;
- return;
- }//end if
- Current=Current->Next;
- }// end while
- }// end if
- else
- { // Rec is to be inserted after Tail
- Current=Tail;
- Rec->Next=Current->Next;
- Rec->Previous=Current;
- if (Current->Next!=NULL) (Current->Next)->Previous=Rec;
- Current->Next=Rec;
- }
- }//end Append
-
-
- void CDBFile::DeleteRecord(Record* Rec)
- { // This function really deletes a record if all the records are loaded
- // in memory and if the whole data is saved to a file (current file
- // or a new one). If only a few records are loaded, it will delete the
- // pointed record from the memory, discarding any changes.
- if (Rec==NULL) return;
- if (Rec==RecordList)
- { RecordList=RecordList->Next;
- if (RecordList) RecordList->Previous=NULL;
- delete(Rec);
- if (FullFileInMemory==TRUE) RecordCount--;
- ModifiedFlag=TRUE;
- return;
- }
- else
- {
- (Rec->Previous)->Next=Rec->Next;
- if (Rec->Next) (Rec->Next)->Previous=Rec->Previous;
- delete(Rec);
- ModifiedFlag=TRUE;
- if (FullFileInMemory==TRUE) RecordCount--;
- }
- }
-
- void CDBFile::AddField(CField* NewField); // Not implemented in this version
-
- void CDBFile::SetFieldValue(Record* Rec, CField* Field, void* Value)
- // Here we have the same problems as in GetFieldValue(), see above.
- {
- char* Data;
- char* RecContents;
- unsigned short FLength;
- unsigned short ResLength;
-
- if (Field->GetType()=='C')
- FLength=Field->GetLength()+256*Field->GetDecCount();
- else
- FLength=Field->GetLength();
-
- RecContents = &(Rec->Contents[Field->GetOffset()]);
- // RecContents points at the beginning of the field within the record.
- Data=new char[FLength+1]; // Data is allocated dynamically
- for (int i=0; i<FLength; i++) Data[i]=' '; // Data is blanked
- Data[FLength]=0; // Last character is set to null, '\0' (end of string)
-
- switch(Field->GetType())
- {
- case 'N': // Numeric
- if (Field->GetDecCount()==0) // Integer (long)
- {
- char *Num=new char[FLength+1];
- long *Result1;
- Result1=(long *)Value;
- ltoa(*Result1, Num, 10);
- ResLength=strlen(Num);
- // Text alignment for numbers is flush-right :
- strncpy(&Data[FLength-ResLength], Num, ResLength);
- strncpy(RecContents, Data, FLength);
- delete []Num;
- break;
- }
- else // Float (double)
- {
- char *Num=new char[FLength+1];
- double *Result1;
- Result1=(double *)Value;
- gcvt(*Result1, FLength,Num);
- ResLength=strlen(Num);
- // Text alignment for numbers is flush-right :
- strncpy(&Data[FLength-ResLength], Num, ResLength);
- strncpy(RecContents, Data, FLength);
- delete []Num;
- break;
- }
- case 'D': // Date : no specific conversion (for portability)
- case 'C': // Character :
- ResLength=strlen((char *)Value);
- strncpy(Data, (char *)Value, ResLength);
- strncpy(RecContents, Data, FLength);
- break;
- case 'L': // Logical : conversion from 'BOOL' type
- BOOL* Result3;
- Result3=(BOOL *)Value;
- if (*Result3==TRUE) Data[0]='Y';
- else Data[0]='N';
- strncpy(RecContents, Data, FLength);
- break;
- case 'M':
- default:
- break;
- }// end switch;
- delete []Data;
- Rec->ModifFlag=TRUE;
- }
-
-
- // Overloaded, public versions of SetFieldValue. :
- void CDBFile::SetFieldValue(char* Field, void* Value)
- { SetFieldValue(CurrentRec,FirstField->GetField(Field), Value);}
-
- void CDBFile::SetFieldValue(unsigned short FieldNum, void* Value)
- { SetFieldValue(CurrentRec,FirstField->GetField(FieldNum), Value);}
-
-
- void CDBFile::DumpCurrentContents(int i, char* String)
- // Dumps the contents of the current record (raw ascii, no formatting)
- {
- if (CurrentRec!=NULL)
- {
- strncpy(String, CurrentRec->Contents, i);
- String[i]=0;
- }
- else String[0]=0;
- }
-
-
-
- Record* CDBFile::ReadRecord(unsigned long RecNum)
- // Reads a record from the file, returns a newly allocated *Record pointer
- // The file offset is determined by RecNum and RecordLength.
- {
- unsigned char Res;
- char* Buffer;
- Record *NewRec;
-
- if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return NULL;
- else
- {
- Buffer=new char[RecordLength];
- fseek (FileHandle, (long)(HeaderSize + (RecNum-1)*RecordLength), SEEK_SET);
- Res=fread (Buffer, RecordLength, 1, FileHandle);
- if (Res==0) return NULL;
- NewRec=new Record;
- NewRec->Contents=Buffer;
- NewRec->ModifFlag=FALSE;
- NewRec->RecordNumber=RecNum;
- NewRec->Next=NULL;
- NewRec->Previous=NULL;
- return NewRec;
- }
- }
-
- BOOL CDBFile::WriteHeader(char* Path)
- // Use that function to rewrite or to save the current file under another
- // name. It writes the header of the current CDBFile. In order to update the
- // "date of last update" field, I introduced two structures : time_t Date, and
- // tm *Date_s, which will have to be changed for a port under UNIX. For more
- // details, have a look at OpenFile() above and the dBase III file structure.
- {
- unsigned char NLength,NDecCount;
- char NType;
- char Written;
- CField *Current;
- char dBaseVersion=0x03;
- char FieldTerm=0x0D;
- time_t Date;
- struct tm *Date_s;
- char Zero[256];
- char Day, Year, Month;
-
- // Initialize a zero buffer;
- for (unsigned short j=0; j<256; j++) Zero[j]=0x00;
-
- // Validity of the path name
- if (Path!=NULL)
- {
- if (strlen(Path)>256)
- {
- fprintf (stderr, "File path too long : %s \n", Path);
- return FALSE;
- }
- strcpy (PathName, Path);
- }
-
- // Close the currently open file
- if (FileHandle!=NULL) fclose(FileHandle);
- FileHandle = fopen (PathName, "w");
- if (FileHandle==NULL) return FALSE;
- fseek (FileHandle, 0L, SEEK_SET);
- Written=fwrite (&dBaseVersion, 1, sizeof (char), FileHandle);
-
-
- time(&Date);
- Date_s = gmtime(&Date);
- Year=(char)(Date_s->tm_year);
- Month=(char)(Date_s->tm_mon);
- Day=(char)(Date_s->tm_mday);
- fseek (FileHandle, 1L, SEEK_SET);
- Written=fwrite (&Year, sizeof (char), 1, FileHandle);
- Written=fwrite (&Month, sizeof (char), 1, FileHandle);
- Written=fwrite (&Day, sizeof (char), 1, FileHandle);
- fseek (FileHandle, 4L, SEEK_SET);
- Written=fwrite (&RecordCount, sizeof (RecordCount), 1, FileHandle);
- fseek (FileHandle, 8L, SEEK_SET);
- Written=fwrite (&HeaderSize, sizeof (HeaderSize), 1, FileHandle);
- Written=fwrite (&RecordLength, sizeof (RecordLength), 1, FileHandle);
- Written=fwrite (&Zero, sizeof(char), 20, FileHandle);
-
- for (unsigned short i=1; i<=FieldCount; i++)
- {
-
- Current=FirstField->GetField(i);
- NLength=Current->GetLength();
- NType=Current->GetType();
- NDecCount=Current->GetDecCount();
- fwrite (Current->GetName(), 11, sizeof (char), FileHandle);
- fwrite (&NType, sizeof (char), 1, FileHandle);
- fwrite (&Zero, sizeof(char), 4, FileHandle);
- fwrite (&NLength, sizeof (unsigned char), 1, FileHandle);
- fwrite (&NDecCount, sizeof (unsigned char), 1, FileHandle);
- fwrite (&Zero, sizeof(char), 14, FileHandle);
- }// end for
- fwrite (&FieldTerm, sizeof (char), 1, FileHandle);
-
- return TRUE;
-
- }
-
- BOOL CDBFile::WriteRecord(Record* Current, unsigned long RecNum)
- // Writes a record at the specified place,using RecNum to calculate the offset
- {
- size_t Res;
-
- if ((FileHandle==NULL)||(RecNum>RecordCount)||(RecNum<1)) return FALSE;
- else
- {
- fseek(FileHandle,(long)(HeaderSize+(RecNum-1)*RecordLength),SEEK_SET);
- Res=fwrite (Current->Contents, RecordLength, 1, FileHandle);
- if (Res==1)
- {
- Current->ModifFlag=FALSE;
- // now that it has been written to file, it's no longer modified.
- return TRUE;
- }
- else return FALSE;
- }
- }
-
- BOOL CDBFile::Swap(Record* Rec1, Record* Rec2)
- // swaps the contents of Rec1 and Rec2 (used in the SortAll function)
- {
- char* BufCont;
- BOOL BufFlag;
-
-
- BufCont=Rec1->Contents;
- BufFlag=Rec1->ModifFlag;
- Rec1->Contents=Rec2->Contents;
- Rec1->ModifFlag=Rec2->ModifFlag;
- Rec2->Contents=BufCont;
- Rec2->ModifFlag=BufFlag;
- return TRUE;
-
- }
-
- BOOL CDBFile::IsBigger(void *v1, void *v2, CField* Criterium)
- // Compares v1 and v2 using Criterium to determine their type.
- {
- switch(Criterium->GetType())
- {
- case 'N': // Numeric
- double *val1, *val2;
- val1=(double*)v1; val2=(double*)v2;
- return(BOOL)(*val1>*val2);
- case 'C':
- default : // Caracter
- char *str1, *str2;
- str1=(char*)v1; str2=(char*)v2;
- return(BOOL)(strcmp(str1, str2)>0);
- }
- }
-
-
- BOOL CDBFile::IsSmaller(void *v1, void *v2, CField* Criterium)
- // Compares v1 and v2 using Criterium to determine their type.
- {
- switch(Criterium->GetType())
- {
- case 'N': // Numeric
- double *val1, *val2;
- val1=(double*)v1; val2=(double*)v2;
- return(BOOL)(*val1<*val2);
- case 'C':
- default : // Caracter
- char *str1, *str2;
- str1=(char*)v1; str2=(char*)v2;
- return(BOOL)(strcmp(str2, str1)>0);
- }
- }
-