Files
harbour-core/harbour/utils/hbdoc/ffile1.prg
2001-04-15 03:04:00 +00:00

643 lines
21 KiB
Plaintext

/*
* $Id$
*/
/*
* Harbour Project source code:
* FFILE1.PRG Filebase class for hbdoc
*
* Copyright 2000 Luiz Rafael Culik <culik@sl.conex.net>
* www - http://www.harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#include "hbclass.ch"
#include 'common.ch'
#include 'hbdocdef.ch'
*+±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
*+
*+ Class FileBase
*+
*+±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
*+
CLASS FileBase FROM FileMan
DATA nOpenMode // Holds the value to use when opening the file
DATA nCreateMode // Holds the value to use when creating the file
DATA nDosHandle // Holds the DOS file handle for this
DATA nEndOfFile // Holds the last byte value in the file
DATA nSkipLength // This holds the default skpping length of 1
DATA cName // This holds the name of the file being worked on
DATA nPosition // This holds the position in the file at
DATA lAtBottom // This is a value to show if at bottom of file
DATA lAtTop // This is a value to show if at top of file
METHOD new( cname ) // This is the constructor for the file
METHOD FOPEN() // This opens the specified file
METHOD closefile() // This closes the specified file
METHOD fskip( n ) // Moves the byte pointer within the file
METHOD FWRITE( c ) // Write passed data to file w/ len() == nSkipLenght
METHOD retrieve() // Returns the contents of the file at current pos
METHOD fgoTop() // Move the byte pointer to the top of the file
METHOD fgoBottom() // Move the byte pointer to the bottom of the file
METHOD fgoto() // Moves the byte pointer to a specific location
METHOD create()
message fappend METHOD fappendByte( cByte )
message BuffGet METHOD BufferGet( lDirection )
METHOD SKIP( nRecord ) // Moves the byte pointer within the file
METHOD WRITE( cChar ) // Write passed data to file w/ len() == nSkipLenght
METHOD goTop() // Move the byte pointer to the top of the file
METHOD goBottom() // Move the byte pointer to the bottom of the file
METHOD GOTO( nValue ) // Moves the byte pointer to a specific location
METHOD OPEN()
message append METHOD appendLine( cline )
ENDCLASS
/* Method: Init/New
Params: N/A
Returns: Self
Purpose: Constructor
*/
METHOD new( cName ) CLASS FileBase
super:new()
// first thing to do is check to see if there is a valid file
::nSkipLength := 1
::nOpenMode := 2 // Mode for which to open the file
::nCreateMode := 0 // Mode for which to create the file
::cName := cName
RETURN ( self )
/* Method: skip( <nRecords> )
Params: <nRecords>
Returns: Self
Purpose: This method moves the file's byte pointer <nRecords> position
from the current location. The actualy movement is determined
on the value of ::nSkipLength which holds the skipping base.
This class's purpose is to do one byte movements.
*/
METHOD fskip( nRecords ) CLASS FileBase
DEFAULT nRecords TO 1
IF ::noDosError() .AND. ::nDosHandle > 0
FSEEK( ::nDosHandle, ( ::nSkipLength * nRecords ), 1 )
::nLastDosMessage := FERROR()
::nPosition := FSEEK( ::nDosHandle, 0, 1 )
DO CASE
CASE ::nPosition == ::nEndOfFile
::lAtBottom := pTRUE
::lAtTop := pFALSE
CASE ::nPosition <= 1
::lAtTop := pTRUE
::lAtBottom := pFALSE
OTHERWISE
::lAtBottom := ::lAtTop := pFALSE
ENDCASE
ENDIF
RETURN ( self )
/* Method: gotop()
Params: N/A
Returns: Self
Purpose: Move the byte pointer to the top of the file
*/
METHOD fgotop() CLASS FileBase
IF ::noDosError() .AND. ::nDosHandle > 0
::nPosition := FSEEK( ::nDosHandle, 0, 0 )
::nLastDosMessage := FERROR()
::lAtTop := pTRUE
::lAtBottom := pFALSE
ENDIF
RETURN ( self )
/* Method: gobottom()
Params: N/A
Returns: Self
Purpose: Move hte byte pointer of the file to tbe bottom.
*/
METHOD fgoBottom() CLASS FileBase
IF ::noDosError() .AND. ::nDosHandle > 0
::nPosition := FSEEK( ::nDosHandle, 0, 2 )
::nLastDosMessage := FERROR()
::lAtTop := pFALSE
::lAtBottom := pTRUE
ENDIF
RETURN ( self )
/* Method: close()
Params: N/A
Returns: Self
Purpose: To close the file
*/
METHOD closefile() CLASS FileBase
IF ::noDosError() .AND. ::nDosHandle > 0
FCLOSE( ::nDosHandle )
::nLastDosMessage := FERROR()
::delItem( ::nDosHandle )
::lAtTop := ::lAtBottom := pFALSE
::nPosition := 0
ENDIF
RETURN ( self )
/* Method: retrieve
Params: N/A
Returns: <cChar>
Purpose: To return the contents of the file at the current position based
on the length of ::nSkipLength.
*/
METHOD retrieve() CLASS FileBase
LOCAL cReturn // as char
LOCAL nMoved // as int
IF ::noDosError() .AND. ::nDosHandle > 0
cReturn := SPACE( ::nSkipLength )
nMoved := FREAD( ::nDosHandle, @cReturn, ::nSkipLength )
::nLastDosMessage := FERROR()
FSEEK( ::nDosHandle, - ( nMoved ), 1 ) // Re-position the pointer
ENDIF
RETURN ( cReturn )
/* Method: write(<cChar>)
Params: <cChar>
Returns: Self
Purpose: To write out to the contents of the file the value in the
parameter <cChar>.
*/
METHOD FWRITE( cChar ) CLASS FileBase
IF ::noDosError() .AND. ::nDosHandle > 0
IF cChar IS pCHARACTER
FWRITE( ::nDosHandle, cChar, 1 )
::nLastDosMessage := FERROR()
IF ::noDosError()
FSEEK( ::nDosHandle, ::nPosition, 0 ) // Re-position the pointer
ENDIF
ENDIF
ENDIF
RETURN ( self )
/* Method: goto(<nRecord>)
Params: <nRecord> The record byte to move to
Returns: Self
Purpose: This method moves the byte marker to the <nRecord> position
within the file. It is also based on the value stored to the
::nSkipLength instance variable
*/
METHOD fgoto( nValue ) CLASS FileBase
IF ::noDosError() .AND. ::nDosHandle > 0
IF nValue IS pNUMERIC
IF nValue > 0 .AND. ;
( nValue * ::nSkipLength ) <= ::nEndOfFile
FSEEK( ::nDosHandle, ( nValue * ::nSkipLength ), 0 )
::nLastDosMessage := FERROR()
::nPosition := FSEEK( ::nDosHandle, 0, 1 )
DO CASE
CASE ::nPosition == ::nEndOfFile
::lAtBottom := pTRUE
::lAtTop := pFALSE
CASE ::nPosition <= 1
::lAtTop := pTRUE
::lAtBottom := pFALSE
OTHERWISE
::lAtBottom := ::lAtTop := pFALSE
ENDCASE
ENDIF
ENDIF
ENDIF
RETURN ( ::nPosition )
/* Method: create()
Params: N/A
Returns: Self
Purpose: Creates the specified file with the proper access code
*/
METHOD Create() CLASS FileBase
LOCAL nFile // as int
IF ::noDosError()
nFile := FCREATE( ::cName, ::nCreateMode )
::nLastDosMessage := FERROR()
IF ::noDosError() // No Error
FCLOSE( nFile ) // Close the file
::fopen() // Re-open the file
ENDIF
ENDIF
RETURN ( self )
/* Method: open()
Params: N/A
Returns: Self
Purpose: Opens the file with the proper access code
*/
METHOD FOPEN() CLASS FileBase
IF ::noDosError()
::nDosHandle :=::openfile( ::cName, ::nOpenMode )
::nEndOfFile := FSEEK( ::nDosHandle, 0, 2 )
::nPosition := FSEEK( ::nDosHandle, 0, 0 )
::lAtTop := pTRUE
::lAtBottom := pFALSE
ENDIF
RETURN ( self )
METHOD fappendByte( cByte ) CLASS FileBase
DEFAULT cByte TO ""
IF !EMPTY( cByte ) // Valid line
IF ::noDosError() .AND. ::nDosHandle > 0 // No error
FSEEK( ::nDosHandle, 0, 2 )
FWRITE( ::nDosHandle, cByte, 1 )
::nEndOfFile := FSEEK( ::nDosHandle, 0, 2 )
::nPosition := FSEEK( ::nDosHandle, - ( LEN( cByte ) ), 2 )
::nSkipLength := LEN( cByte )
::lAtBottom := ::lAtTop := pFALSE
ENDIF
ENDIF
RETURN ( self )
// End of File: FFile1.prg
METHOD OPEN() CLASS FileBase
Self:nDosHandle := Self:openfile( ::cName, ::nOpenMode )
::nEndOfFile := FSEEK( Self:nDosHandle, 0, 2 )
FSEEK( Self:nDosHandle, 0, 0 )
::nSkipLength := Self:Buffget()
::lAtTop := pTRUE
::lAtBottom := pFALSE
::nHan := Self:nDosHandle
RETURN ( self )
/* Method: gotop()
Params: N/A
Returns: Self
Purpose: Move the byte pointer to the top of the file
*/
METHOD gotop() CLASS FileBase
IF Self:noDosError() .AND. Self:nDosHandle > 0
::fgotop()
::nSkipLength := Self:Buffget()
ENDIF
RETURN ( self )
/* Method: gobottom()
Params: N/A
Returns: Self
Purpose: Move hte byte pointer of the file to tbe bottom.
*/
METHOD goBottom() CLASS FileBase
LOCAL cBuffer // as char
LOCAL lWithCRLF := pFALSE // as logical
IF Self:noDosError() .AND. Self:nDosHandle > 0
::fgobottom()
// Now, back off from the end one line length and set the marker
cBuffer := SPACE( pBUFFER_LENGTH )
FSEEK( Self:nDosHandle, - ( pBUFFER_LENGTH ), 2 )
FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH )
IF RIGHT( cBuffer, 2 ) == pCRLF // We need to remove this extra one!
cBuffer := LEFT( cBuffer, LEN( cBuffer ) - 2 )
lWithCRLF := pTRUE
ENDIF
cBuffer := SUBSTR( cBuffer, RAT( pCRLF, cBuffer ) + 2 )
::nSkipLength := LEN( cBuffer ) + IF( lWithCRLF, 2, 0 )
::nposition := FSEEK( Self:nDosHandle, - ( LEN( cBuffer ) ), 2 )
IF lWithCRLF
::nposition := FSEEK( Self:nDosHandle, - 2, 1 )
ENDIF
ENDIF
RETURN ( self )
/* Method: close()
Params: N/A
Returns: Self
Purpose: To close the file
*/
METHOD FCLOSE() CLASS FileBase
IF Self:noDosError() .AND. Self:nDosHandle > 0
FCLOSE( Self:nDosHandle )
Self:nLastDosMessage := FERROR()
Self:delItem( Self:nDosHandle )
Self:lAtTop := Self:lAtBottom := pFALSE
Self:nPosition := 0
ENDIF
RETURN ( self )
/* Method: write(<cChar>)
Params: <cChar>
Returns: Self
Purpose: To write out to the contents of the file the value in the
parameter <cChar>.
*/
METHOD WRITE( cChar ) CLASS FileBase
IF Self:noDosError() .AND. Self:nDosHandle > 0
IF cChar IS pCHARACTER
IF cChar > Self:nSkipLength // we are going to truncate for now...
FWRITE( Self:nDosHandle, cChar, Self:nSkipLength )
ELSE
FWRITE( Self:nDosHandle, cChar, LEN( cChar ) )
ENDIF
FSEEK( Self:nDosHandle, ::nposition, 0 )
Self:nLastDosMessage := FERROR()
IF Self:noDosError()
FSEEK( Self:nDosHandle, Self:nPosition, 0 ) // Re-position the pointer
ENDIF
ENDIF
ENDIF
RETURN ( self )
/* Method: getBuffer( <lDirection> )
Params: <lDirection> Logical toggle for direction
Returns: <nBytes>
Purpose: To return the number of bytes either forward or backward from
the present file pointer position in which the next CRLF char
appears. If <lDirection> is a logical false (.F.) value, them
the operation will go in reverse order; otherwise, it will go
in a forward direction. The default value is a logical true
(.T.) value.
*/
METHOD Buffget( lForward ) CLASS FileBase
LOCAL cBuffer // as char
LOCAL nLocation // as int
LOCAL nRead // as int
LOCAL lWithCRLF := pFALSE // as logical
DEFAULT lForward TO pTRUE
IF !lForward
nRead := FSEEK( Self:nDosHandle, ;
- ( IF( ::nposition < pBUFFER_LENGTH, ;
::nposition, ;
pBUFFER_LENGTH ) ), ;
1 ) // rewind backwards
cBuffer := SPACE( ::nposition - nRead )
FREAD( Self:nDosHandle, @cBuffer, ( ::nposition - nRead ) )
IF RIGHT( cBuffer, 2 ) == pCRLF // with line already
cBuffer := LEFT( cBuffer, LEN( cBuffer ) - 2 )
lWithCRLF := pTRUE
ENDIF
nLocation := LEN( cBuffer ) - ( RAT( pCRLF, cBuffer ) )
ELSE
cBuffer := SPACE( pBUFFER_LENGTH )
nRead := FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH )
FSEEK( Self:nDosHandle, - ( IF( nRead < pBUFFER_LENGTH, nRead, ;
pBUFFER_LENGTH ) ), 1 ) // Rewind
// Now, parse the string. and file
nLocation := AT( pCRLF, cBuffer )
// Now, if there is NO CRLF in the buffer and if the value of the
// number of bytes read is less than the buffer length, then we
// have an end of file condition.
IF nLocation == 0 .AND. ( nRead < pBUFFER_LENGTH )
// If so, then set the appropriate flags accordingly.
::lAtBottom := pTRUE
::lAtTop := pFALSE
ENDIF
ENDIF
RETURN ( nLocation )
/* Method: appendLine( <cLine )
Params: <cLine> Character line to append
Returns: Self
Purpose: To append a blank CRLF delimited line at the end of the file.
If <cLine> is not passed or if it an empty line with 0 bytes
in length, the function will not operate.
*/
METHOD appendLine( cLine ) CLASS FileBase
DEFAULT cLine TO ""
IF LEN( cLine ) == 0 // Valid line
IF Self:noDosError() .AND. Self:nDosHandle > 0 // No error
IF !( pCRLF $ cLine ) // No CRLF, so add
cLIne += pCRLF
ENDIF
FSEEK( Self:nDosHandle, 0, 2 )
FWRITE( Self:nDosHandle, cLine )
::nEndOfFile := FSEEK( Self:nDosHandle, 0, 2 )
::nposition := FSEEK( Self:nDosHandle, - ( LEN( cLine ) ), 2 )
::nSkipLength := LEN( cLine )
::lAtBottom := ::lAtTop := pFALSE
ENDIF
ENDIF
RETURN ( self )
/* Method: skip( <nRecords> )
Params: <nRecords>
Returns: Self
Purpose: This method moves the file's byte pointer <nRecords> position
from the current location. The actualy movement is determined
on the value of Self:nSkipLength which holds the skipping base.
This class's purpose is to do one byte movements.
*/
METHOD SKIP( nRecords ) CLASS FileBase
LOCAL nCount := 0 // as int
DEFAULT nRecords TO 1
// Here, we have to start looking for CHR(13)+CHR(10) character
// combinations. Once found, then we have to set the super class
// values appropriately
IF Self:noDosError() .AND. Self:nDosHandle > 0
DO CASE
CASE nRecords > 0 // It's positive movement
WHILE nCount ++ != nRecords
::fskip()
::nSkipLength := Self:Buffget()
ENDDO
CASE nRecords < 0 // It's negative movement
WHILE nCount -- != nRecords
::nSkipLength := Self:Buffget( pFALSE )
::fskip( - 1 )
ENDDO
ENDCASE
ENDIF
RETURN ( self )
/* Method: goto(<nRecord>)
Params: <nRecord> The record byte to move to
Returns: Self
Purpose: This method moves the byte marker to the <nRecord> position
within the file. It is also based on the value stored to the
Self:nSkipLength instance variable
*/
METHOD GOTO( nValue ) CLASS FileBase
LOCAL cLine := "" // as char
LOCAL nCount := 0 // as int
LOCAL lContinue := pTRUE // as logical
LOCAL cBuffer // as char
DEFAULT nValue TO 0
IF Self:noDosError() .AND. Self:nDosHandle > 0
IF nValue IS pNUMERIC
IF nValue > 0 // o.k. so far
FSEEK( Self:nDosHandle, 0, 0 ) // start at the top
WHILE lContinue
cBuffer := SPACE( pBUFFER_LENGTH )
lContinue := ( FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH ) == ;
pBUFFER_LENGTH )
cBuffer := cLine + cBuffer
WHILE pCRLF $ cBuffer
IF ++ nCount == nValue
lContinue := pFALSE
EXIT
ENDIF
cBuffer := SUBSTR( cBuffer, AT( pCRLF, cBuffer ) + 2 )
ENDDO
cLine := cBuffer
ENDDO
IF nCount == nValue // We have a match
FSEEK( Self:nDosHandle, - ( pBUFFER_LENGTH ), 1 ) // Back off from here
::nposition := FSEEK( Self:nDosHandle, ;
( pBUFFER_LENGTH - LEN( cBuffer ) ), ;
1 ) // Move
::nSkipLength := Self:Buffget()
ELSE
FSEEK( Self:nDosHandle, ::nposition, 0 )
nCount := 0
ENDIF
ENDIF
ENDIF
ENDIF
RETURN ( nCount )
// End of File: FFile2.prg
METHOD BufferGet( lForward ) CLASS FileBase
LOCAL cBuffer // as char
LOCAL nLocation // as int
LOCAL nRead // as int
LOCAL lWithCRLF := pFALSE // as logical
DEFAULT lForward TO pTRUE
IF !lForward
nRead := FSEEK( Self:nDosHandle, ;
- ( IF( ::nposition < pBUFFER_LENGTH, ;
::nposition, ;
pBUFFER_LENGTH ) ), ;
1 ) // rewind backwards
cBuffer := SPACE( ::nposition - nRead )
FREAD( Self:nDosHandle, @cBuffer, ( ::nposition - nRead ) )
IF RIGHT( cBuffer, 2 ) == pCRLF // with line already
cBuffer := LEFT( cBuffer, LEN( cBuffer ) - 2 )
lWithCRLF := pTRUE
ENDIF
nLocation := LEN( cBuffer ) - ( RAT( pCRLF, cBuffer ) )
ELSE
cBuffer := SPACE( pBUFFER_LENGTH )
nRead := FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH )
FSEEK( Self:nDosHandle, - ( IF( nRead < pBUFFER_LENGTH, nRead, ;
pBUFFER_LENGTH ) ), 1 ) // Rewind
// Now, parse the string. and file
nLocation := AT( pCRLF, cBuffer )
// Now, if there is NO CRLF in the buffer and if the value of the
// number of bytes read is less than the buffer length, then we
// have an end of file condition.
IF nLocation == 0 .AND. ( nRead < pBUFFER_LENGTH )
// If so, then set the appropriate flags accordingly.
::lAtBottom := pTRUE
::lAtTop := pFALSE
ENDIF
ENDIF
RETURN ( nLocation )
*+ EOF: FFILE1.PRG