home *** CD-ROM | disk | FTP | other *** search
- //****************************************************************************
- //
- // FILE: DBFile.cpp
- //
- // WRITTEN BY: Keimpe
- //
- // DATE: 1/94
- //
- // UPDATED: 5/95
- //
- // REVISION: $Revision: 2.12 $
- //
- // VERSION: Visual dBASE
- //
- // DESCRIPTION:
- //
- // Main source file for dbfile, a Visual dBASE example.
- //
- // This example implements a series of textfile manipulation routines. These
- // routines are accessible from Visual dBASE through the EXTERN system.
- // A Textfile object on the Visual dBASE side initiates the contact by creating
- // a corresponding C++ Textfile object through a call to TFinit which
- // returns a pointer to that C++ object to the Visual dBASE object. After that
- // the Visual dBASE object can call into this DLL for specific routines. The
- // C++ routine calls back to the Visual dBASE object through DBase->GetThis()
- // to get the this pointer of the corresponding C++ object and calls
- // the wanted memberfunction of that object.
- //
- // Right now the available routines are:
- // close - closes the current file if any.
- // eof - helps detect EOF.
- // error - tells if an error was encountered.
- // fieldseparator - sets the field separator.
- // filter - sets the filter string.
- // geterror - returns the last error encountered if any.
- // getfield - gets the next field of the current record.
- // getline - gets the next line.
- // getrec - gets the next record.
- // lineseparator - sets the line separator.
- // open - opens a file.
- // release - releases the C++ object.
- //
- // A line is ended by a lineseparator or EOF. If you add a '+' char
- // to the char you send to "SetLineSeparator" it is assumed that
- // the lineseparator is one or more char's of that lineseparator.
- // A rec is a line that contains fields. Getrec sets up the fields
- // and returns the number of fields in the current rec. A field is
- // surrounded by one or more field separators including as special
- // cases the start and the end of a line.
- // If the filter is set a getline or getrec will search till
- // a line is found that contains the filter string as a substring.
- //
- // The file dbfile.h will give a quick idea of how the C++ class is
- // set up. The file dbfile.prg will tell you how things work on the
- // Visual dBASE side. The file dbasevar.h deals with the basic classes
- // that ease the communication between C++ and Visual dBASE.
- //
- //****************************************************************************
-
- // Get the header.
- #include "dbfile.h"
-
- // If you want to test the TextFile C++ class under DOS, define DOSTEST.
- // This will isolate the class from any Windows and Visual dBASE details and
- // allows this file to be compiled and linked with DOS programs.
- #ifndef DOSTEST
-
- //============================================================================
- //
- // You can create an instance of class CSession in DBaseInitInstance()
- // and set it up. In subsequent call backs, a pointer to this instance
- // can be retrieved with a call to GetSession(). Data that is kept on
- // a per-instance basis (DBaseVars for example) should be kept in the
- // session object.
- //
- //============================================================================
-
- class CSession {
- public:
-
- // Local objects.
-
- CSession(){
- // Initialize objects local to this session.
- }
- ~CSession(){
- // Destroy objects local to this session.
- }
-
- // Memberfunctions to use local objects.
- };
-
-
- extern "C" {
-
- //============================================================================
- //
- // Usual DLL functions.
- //
- //============================================================================
-
- int FAR PASCAL LibMain(HINSTANCE, WORD, WORD, LPSTR){
-
- return 1;
- }
- int CALLBACK WEP(int /*nParam*/){return(1);}
-
- //
- // This function is called every time an Instance of Visual dBASE loads the DLL.
- // If for some reason the DLL determines that it cannot load, it can return
- // an error. If the DLL loads properly it should return DBASE_INIT_OK.
- //
-
- int CALLBACK DBaseInitInstance(void){
-
- asm int 3; // break point for the debugger.
-
- DBase()->SetSession(new CSession());
-
- //
- // Possible Error return Values
- //
- //return DBASE_INIT_NO_MULTI_INSTANCE;
- //return DBASE_INIT_ERROR;
-
- return DBASE_INIT_OK;
- }
-
- //
- // DBaseExitInstance() is called when an instance of Visual dBASE terminates or
- // manually unloads a DLL through the RELEASE DLL command. This give the
- // DLL a chance to free any resources associated with a running instance.
- //
-
- void CALLBACK DBaseExitInstance(void){
- }
-
- // End of DOSTEST define
- #endif
-
- //============================================================================
- //
- // Implementation of memberfunctions of the TextFile class.
- //
- //============================================================================
-
- // Some common defines.
- #define LINE 0
- #define REC 1
-
- //
- // AddToText( char*, char * ).
- //
- // Helper function to append a char string to the Text string. The
- // Text buffer grows as needed to fit the largest rec or line
- // encountered in the file.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL TextFile::AddToText( char * Begin, char * End ) {
-
- unsigned int Len;
- char SaveIt = *End;
-
- // Zero terminate the incoming string.
- *End = 0x0;
- Len = strlen( Begin );
-
- // See if we need more room.
- if( (TextLen + Len) > MaxTextLen ) {
- char * NewText;
-
- #ifdef NO_EXCEPTIONS
-
- MaxTextLen = TextLen + Len;
- NewText = new char[ MaxTextLen + 1 ];
- if( NewText != NULL ) {
- strcpy( NewText, Text );
- delete [] Text;
- Text = NewText;
- }
- else {
- // Must be something wrong.
- Text[0] = 0x0;
- TextLen = 0;
- *End = SaveIt;
- return 0;
- }
-
- #else // NO_EXCEPTIONS
-
- try {
- MaxTextLen = TextLen + Len;
- NewText = new char[ MaxTextLen + 1 ];
- strcpy( NewText, Text );
- delete [] Text;
- Text = NewText;
- }
- catch(...) {
-
- // Must be something wrong.
- Text[0] = 0x0;
- TextLen = 0;
- *End = SaveIt;
- return 0;
- }
-
- #endif // NO_EXCEPTIONS
-
- }
- // Now simply copy it in.
- strcpy( Text + TextLen, Begin );
- TextLen += Len;
-
- // Restore.
- *End = SaveIt;
-
- // Return success.
- return 1;
- }
-
- //
- // Close().
- //
- // Closes the file and sets Handle to 0.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL TextFile::Close() {
-
- if( Handle != 0 )
- if( close( Handle ) != 0 )
- return 0;
-
- Handle = 0;
-
- // return success.
- return 1;
- }
-
- //
- // GetField( int ).
- //
- // This returns a pointer to the field specified by the WhichField
- // parameter. The fields are contained inside a rec separated by
- // single fieldseparators. When you send a field away, you simply insert
- // a zero at the end of that field. Next time you come back here you
- // put that separator back which you can always find as the first char
- // of the Text buffer.
- //
- // Returns pointer to field, or pointer to empty string in case of error.
- //
-
- char * TextFile::GetField( int WhichField ) {
-
- char *FieldStart = Text + 1, *FieldEnd = Text + 1, Separator = Text[0];
-
- // First see if we have to reinsert the end of a previous
- // field we sent away.
- if( SaveFieldEndPtr != NULL ) {
- *SaveFieldEndPtr = Text[0];
- SaveFieldEndPtr = NULL;
- }
-
- // Check if we have the field at all.
- if( WhichField <= 0 || WhichField > NumberOfFields )
- return "";
-
- // Find the field in the Text buffer.
- while( --WhichField > 0 ) {
- FieldStart = strchr( FieldStart, Separator );
- if( FieldStart == NULL ) {
- strcpy( ErrorString, "Record corrupted" );
- return "";
- }
- FieldStart++;
- }
-
- // Find the end and mark it.
- FieldEnd = strchr( FieldStart, Separator );
- if( FieldEnd != NULL ) {
- SaveFieldEndPtr = FieldEnd;
- *FieldEnd = 0x0;
- }
-
- // Send it away.
- return FieldStart;
- }
-
-
- //
- // Open( char* ).
- //
- // If a file was already open, that file is closed before the
- // requested file is opened. Handle is set to the file.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL TextFile::Open( char *FileToOpen ) {
-
- // Is there already a file open.
- // If so, close it. Check for proper return.
- if( Handle != 0 )
- if( close( Handle ) != 0 )
- return 0;
-
- // Now open the file requested. Check for valid pointer return.
- if( ( Handle = open( FileToOpen, O_RDONLY | O_TEXT ) ) == -1 )
- return 0;
-
- // Start a new errorstring. Set up the Buffer.
- ErrorString[ 0 ] = 0x0;
- BufPtr = Buffer;
- Buffer[ 0 ] = 0x0;
-
- // Return success.
- return 1;
- }
-
- //
- // ReadNextItem( char ).
- //
- // Reads the next item, either a line or a rec as indicated by the
- // LineOrRec parameter, into the Text character buffer. The file
- // is buffer by buffer read into Buffer. Buffer is scanned for
- // separators and EOF. After a field separator is found we handle
- // field details if we're reading a rec. After we find a line separator
- // the BufPtr is set to after the separator for the next readitem.
- // The Buffer is flushed into the Text buffer after each field and
- // line or when the Buffer runs out. At the end a complete next
- // line or rec will be in the Text buffer.
- //
- // Returns Text on success, "" on error.
- //
-
- char * TextFile::ReadNextItem( char LineOrRec ) {
-
- char Done, Found = 0, StartOfRec;
- unsigned int NumberOfBytes;
- char BothSeparators[3] = { LineSeparator, 0x0, 0x0 };
- char * SepPtr = NULL, SepFound = 0x0;
-
- // In error state?
- if( Error() )
- return "";
-
- // Are we in business already?
- if( Handle == 0 ) {
- strcpy( ErrorString, "No file open\n" );
- return "";
- }
-
- // If we're scanning for a rec, we need the fieldseparator.
- if( LineOrRec == REC )
- BothSeparators[1] = FieldSeparator;
-
- // Keep reading till we found the next item.
- while( !Found ) {
-
- // Set up for the search and initialize Text.
- Done = 0;
- Text[0] = 0x0;
- TextLen = 0;
- NumberOfFields = 0;
- SaveFieldEndPtr = NULL;
- StartOfRec = (LineOrRec == REC );
-
- // Read and scan till we find a separator or EOF.
- while( !Done ) {
-
- // Did we run out of chars in the Buffer.
- if( *BufPtr == 0x0 ) {
-
- // Read in a buffer full.
- NumberOfBytes = (unsigned int) read( Handle,
- Buffer, BUFFERLENGTH );
-
- // Check for EOF or error.
- if( NumberOfBytes == (unsigned int) -1 ||
- NumberOfBytes == 0 ) {
- if( NumberOfBytes != 0 ) {
- strcpy( ErrorString, "Error Reading\n" );
- return ""; // Error.
- }
- Done = 1; // EOF.
-
- // If there's nothing in the textbuffer we
- // can't find a next item.
- if( Text[0] == 0x0 ) {
- Found = 1;
- }
- // But if there is something check if we have
- // the last field of the file that we haven't
- // counted yet.
- else if( LineOrRec == REC && SepFound == 0x0 )
- NumberOfFields++;
-
- continue;
- }
-
- // Set up for the search.
- BufPtr = Buffer;
- *( BufPtr + NumberOfBytes ) = 0x0;
- CopyPtr = Buffer;
- }
-
- // If we scan for a REC, first eat any fieldseparators
- // at the beginning. If we run out of Buffer, fall
- // through and read more. We also put the current
- // fieldseparator as the first char of the Text.
- if( StartOfRec == 1 ) {
-
- while( *BufPtr == FieldSeparator )
- BufPtr++;
- if( *BufPtr == 0x0 )
- continue;
-
- StartOfRec = 0;
- Text[ 0 ] = FieldSeparator;
- TextLen++;
- CopyPtr = BufPtr;
- }
-
- // Look for the next separator IF we're not already
- // inside a separator.
- if( SepFound == 0x0 ) {
-
- // Cruise till we find a separator.
- SepPtr = strpbrk( BufPtr, BothSeparators );
-
- // If we found a separator.
- if( SepPtr != NULL ) {
-
- // Mark the end.
- BufPtr = SepPtr;
- SepFound = *SepPtr;
-
- // If we are looking for fields.
- if( LineOrRec == REC ) {
-
- // Increase number of fields. Send one
- // fieldseparator to text as well IF
- // we found a fieldseparator.
- NumberOfFields++;
- if( SepFound == FieldSeparator )
- BufPtr++;
- }
- }
- else { // We did not find a separator. Adjust BufPtr.
-
- BufPtr += strlen( BufPtr );
- }
-
- // Add what we have to Text.
- if( AddToText( CopyPtr, BufPtr ) == 0 ) {
-
- // Error occured.
- strcpy( ErrorString, "Line/Field too long" );
- return "";
- }
- }
-
- // If we found a separator ( or are inside a separator ).
- if( SepFound != 0x0 ) {
-
- // If we found a lineseparator and only singles of
- // those are allowed, simply skip one and continue.
- if( SepFound == LineSeparator && PlusLineSeparator == 0 ) {
- BufPtr++;
- SepFound = 0x0;
- Done = 1;
- CopyPtr = BufPtr;
- continue;
- }
-
- // Now we scan till we run out of separators.
- while( *BufPtr == SepFound )
- BufPtr++;
-
- // If we found the end of the buffer, fall
- // through and continue reading.
- if( *BufPtr == 0x0 )
- continue;
-
- // Special case for when we found a lineseparator
- // after a bunch of fieldseparators. Means we have
- // to fall through and scan for the end of the rec.
- if( *BufPtr == LineSeparator ) {
- SepFound = LineSeparator;
- continue;
- }
-
- // We're done IF we were looking for lineseparator(s).
- if( SepFound == LineSeparator )
- Done = 1;
-
- // Either way, we're done with the separator found.
- // And we can adjust copyptr.
- SepFound = 0x0;
- CopyPtr = BufPtr;
- }
- }
-
- // Now see if a filter is in place.
- if( Filter[0] != 0x0 ) {
- if( strstr( Text, Filter ) != NULL )
- Found = 1;
- }
- else {
- // Otherwise we have found what we wanted.
- Found = 1;
- }
- }
-
- // Return success.
- return Text;
- }
-
- //
- // SetFilter( char* ).
- //
- // Sets the Filter char string. ReadNextItem only returns the next
- // item IF it contains the Filter string as a substring.
- // Can be called with SetFilter( "" ) to set the filter to nothing.
- //
- // Returns 1 on success. 0 on error.
- //
-
- BOOL TextFile::SetFilter( char *InFilter ) {
-
- unsigned int Len = strlen( InFilter );
-
- // If there's enough room, copy it in and leave.
- if( Len <= FilterLen ) {
- strcpy( Filter, InFilter );
- return 1;
- }
- // Not enough room, start with a clean slate.
- delete [] Filter;
-
- // Create some room.
-
- #ifdef NO_EXCEPTIONS
-
- Filter = new char [ Len + 1 ];
- if( Filter != NULL ){
- FilterLen = Len;
- }
- else {
- // Guess not.
- strcpy( ErrorString, "Filter too long" );
- FilterLen = 0;
- return 0;
- }
-
- #else // NO_EXCEPTIONS
-
- try {
- Filter = new char [ Len + 1 ];
- FilterLen = Len;
- }
- catch(...) {
- // Guess not.
- strcpy( ErrorString, "Filter too long" );
- FilterLen = 0;
- return 0;
- }
-
- #endif // NO_EXCEPTIONS
-
- strcpy( Filter, InFilter );
-
- // Success.
- return 1;
- }
-
- //
- // SetFieldSeparator( char * ).
- //
- // Sets the fieldseparator char.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL TextFile::SetFieldSeparator( char *Separator ) {
-
- // If nothing there, ignore, but return error.
- if( Separator == NULL )
- return 0;
-
- // Set the FieldSeparator.
- FieldSeparator = *Separator;
-
- // We did it.
- return 1;
- }
-
- //
- // SetLineSeparator( char * ).
- //
- // Sets the lineseparator char. If the second char of the incoming
- // string is a '+' it is assumed that the separator is multiples of
- // the lineseparator char.
- //
- // ASSUMES a string is sent over, not a single character!!
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL TextFile::SetLineSeparator( char *Separator ) {
-
- // If nothing there, ignore, but return error.
- if( Separator == NULL )
- return 0;
-
- // Set the LineSeparator and PlusLineSeparator if needed.
- LineSeparator = *Separator;
- if( *(Separator+1) == '+' )
- PlusLineSeparator = 1;
- else
- PlusLineSeparator = 0;
-
- // We did it.
- return 1;
- }
-
- // If you want to test the C++ class under DOS, define DOSTEST.
- #ifndef DOSTEST
-
- //============================================================================
-
- //
- // TextFile routines called from the Visual dBASE side.
- //
-
- //============================================================================
-
- //
- // GetCPlusPlusThis().
- //
- // Helper function to get the C++ this pointer out of the Visual dBASE object.
- //
- // Returns a TextFile pointer.
- //
-
- TextFile* GetCPlusPlusThis() {
-
- // Local vars. Get the this of the Visual dBASE object.
- DVar CThis, DBaseThis( DBase()->GetThis() );
-
- // Get the this of the corresponding C++ object.
- DBaseThis->Property( "MYSTRUCT", CThis );
-
- return (TextFile*)(CThis->Long());
- }
-
- //
- // TFclose().
- //
- // Closes the file.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL _export _pascal TFclose() {
-
- return GetCPlusPlusThis()->Close();
- }
-
- //
- // TFeof().
- //
- // Returns 1 on EOF, 0 otherwise.
- //
-
- BOOL _export _pascal TFeof() {
-
- return GetCPlusPlusThis()->Eof();
- }
-
- //
- // TFerror().
- //
- // Returns 1 on error set, 0 otherwise.
- //
-
- BOOL _export _pascal TFerror() {
-
- return GetCPlusPlusThis()->Error();
- }
-
- //
- // TFfilter( char* ).
- //
- // Sets the filter.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL _export _pascal TFfilter( char * Filter ) {
-
- return GetCPlusPlusThis()->SetFilter( Filter );
- }
-
- //
- // TFfieldseparator( char* ).
- //
- // Sets the fieldseparator char.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL _export _pascal TFfieldseparator( char *InString ) {
-
- return GetCPlusPlusThis()->SetFieldSeparator( InString );
- }
-
- //
- // TFgeterror().
- //
- // Returns pointer to the errorstring of the object.
- //
-
- char * _export _pascal TFgeterror() {
-
- return GetCPlusPlusThis()->GetErrorString();
- }
-
- //
- // TFgetfield( int ).
- //
- // Returna a pointer to the field as indicated by the parameter.
- //
- // Returns pointer to a field on success, "" on error.
- //
-
- char * _export _pascal TFgetfield( int WhichField ) {
-
- return GetCPlusPlusThis()->GetField( WhichField );
- }
-
- //
- // TFgetrec()
- //
- // Returns # of fields in the next rec on success, 0 on error or EOF.
- //
-
- int _export _pascal TFgetrec() {
-
- TextFile * TFptr = GetCPlusPlusThis();
- if( TFptr->ReadNextItem( REC ) != NULL )
- return TFptr->GetNumberOfFields();
- else
- return 0;
- }
-
- //
- // TFgetline().
- //
- // Returns pointer to the next line on success, "" on error or on EOF.
- //
-
- char * _export _pascal TFgetline() {
-
- return GetCPlusPlusThis()->ReadNextItem( LINE );
- }
-
- //
- // TFinit().
- //
- // Initialization routine called from Visual dBASE when the Visual dBASE
- // textfile object gets created.
- //
- // Return pointer (cast to a long) to a C++ TextFile object on success,
- // 0 on error.
- //
-
- long _export _pascal TFinit() {
-
- TextFile * ThisTextFile;
-
- #ifdef NO_EXCEPTIONS
-
- // Get the this of the CTextFile to be used.
- ThisTextFile = new TextFile;
- if( ThisTextFile != NULL ) {
-
- // Return pointer cast to a long.
- return (long) ThisTextFile;
- }
- else {
-
- // Failure.
- return 0L;
- }
-
- #else // NO_EXCEPTIONS
-
- try {
-
- // Get the this of the CTextFile to be used.
- ThisTextFile = new TextFile;
-
- // Return pointer cast to a long.
- return (long) ThisTextFile;
- }
- catch( ... ) {
-
- // Failure.
- return 0L;
- }
-
- #endif // NO_EXCEPTIONS
-
- }
-
- //
- // TFlineseparator( char* ).
- //
- // Sets the line separator char.
- //
- // Returns 1 on success, 0 on error.
- //
-
- BOOL _export _pascal TFlineseparator( char *InString ) {
-
- return GetCPlusPlusThis()->SetLineSeparator( InString );
- }
-
- //
- // TFopen( char* ).
- //
- // Opens a new file.
- //
- // Returns 1 on sucess, 0 on error.
- //
-
- BOOL _export _pascal TFopen( char * FileToOpen ) {
-
- return GetCPlusPlusThis()->Open( FileToOpen );
- }
-
- //
- // TFrelease().
- //
- // Releases the C++ TextFile object.
- //
- // Returns 1 on sucess, 0 on error.
- //
-
- void _export _pascal TFrelease() {
-
- delete GetCPlusPlusThis();
- }
-
- } // extern "C"
-
- // End of DOSTEST define
- #endif
-