* common.mak
* source/vm/debug.c
* source/vm/hvm.c
* source/vm/Makefile
+ source/vm/debugold.c
+ Added some HB_DBG_*() functions for compatibility
with certain 3rd party libs. They are marked with
HB_LEGACY_LEVEL, and will be removed (pbly at 1.1.0),
so everyone is encouraged to use __DBG*() version
for Harbour versions above 0x010000 (or not use
them at all, if this is possible). If these functions
are indeed needed for some functionality, it'd be
good to hear about it, maybe there is a solution
which doesn't require internals.
* utils/hbdoc/ft_funcs.prg
* utils/hbmake/ft_funcs.prg
* Using FS_* manifest constants.
990 lines
31 KiB
Plaintext
990 lines
31 KiB
Plaintext
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* File support Functions 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 "fileio.ch"
|
|
|
|
#define pBUFFER_LENGTH 4096
|
|
|
|
STATIC s_oFileBase
|
|
|
|
*+--------------------------------------------------------------------
|
|
*+
|
|
*+ 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 := FO_READWRITE // Mode for which to open the file
|
|
::nCreateMode := FC_NORMAL // 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 ), FS_RELATIVE )
|
|
::nLastDosMessage := FERROR()
|
|
::nPosition := FSEEK( ::nDosHandle, 0, FS_RELATIVE )
|
|
DO CASE
|
|
CASE ::nPosition == ::nEndOfFile
|
|
::lAtBottom := .T.
|
|
::lAtTop := .F.
|
|
CASE ::nPosition <= 1
|
|
::lAtTop := .T.
|
|
::lAtBottom := .F.
|
|
OTHERWISE
|
|
::lAtBottom := ::lAtTop := .F.
|
|
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, FS_SET )
|
|
::nLastDosMessage := FERROR()
|
|
::lAtTop := .T.
|
|
::lAtBottom := .F.
|
|
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, FS_END )
|
|
::nLastDosMessage := FERROR()
|
|
::lAtTop := .F.
|
|
::lAtBottom := .T.
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* Method: closefile()
|
|
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 := .F.
|
|
::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 ), FS_RELATIVE ) // 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 ISCHARACTER( cChar )
|
|
FWRITE( ::nDosHandle, cChar, 1 )
|
|
::nLastDosMessage := FERROR()
|
|
IF ::noDosError()
|
|
FSEEK( ::nDosHandle, ::nPosition, FS_SET ) // 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 ISNUMBER( nValue )
|
|
IF nValue > 0 .AND. ;
|
|
( nValue * ::nSkipLength ) <= ::nEndOfFile
|
|
FSEEK( ::nDosHandle, ( nValue * ::nSkipLength ), FS_SET )
|
|
::nLastDosMessage := FERROR()
|
|
::nPosition := FSEEK( ::nDosHandle, 0, FS_RELATIVE )
|
|
DO CASE
|
|
CASE ::nPosition == ::nEndOfFile
|
|
::lAtBottom := .T.
|
|
::lAtTop := .F.
|
|
CASE ::nPosition <= 1
|
|
::lAtTop := .T.
|
|
::lAtBottom := .F.
|
|
OTHERWISE
|
|
::lAtBottom := ::lAtTop := .F.
|
|
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, FS_END )
|
|
::nPosition := FSEEK( ::nDosHandle, 0, FS_SET )
|
|
::lAtTop := .T.
|
|
::lAtBottom := .F.
|
|
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, FS_END )
|
|
FWRITE( ::nDosHandle, cByte, 1 )
|
|
::nEndOfFile := FSEEK( ::nDosHandle, 0, FS_END )
|
|
::nPosition := FSEEK( ::nDosHandle, - ( LEN( cByte ) ), FS_END )
|
|
::nSkipLength := LEN( cByte )
|
|
::lAtBottom := ::lAtTop := .F.
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
METHOD OPEN() CLASS FileBase
|
|
|
|
Self:nDosHandle := Self:openfile( ::cName, ::nOpenMode )
|
|
::nEndOfFile := FSEEK( Self:nDosHandle, 0, FS_END )
|
|
FSEEK( Self:nDosHandle, 0, FS_SET )
|
|
::nSkipLength := Self:Buffget()
|
|
::lAtTop := .T.
|
|
::lAtBottom := .F.
|
|
::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 := .F. // 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 ), FS_END )
|
|
FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH )
|
|
IF RIGHT( cBuffer, 2 ) == hb_OSNewLine() // We need to remove this extra one!
|
|
cBuffer := LEFT( cBuffer, LEN( cBuffer ) - 2 )
|
|
lWithCRLF := .T.
|
|
ENDIF
|
|
cBuffer := SUBSTR( cBuffer, RAT( hb_OSNewLine(), cBuffer ) + 2 )
|
|
::nSkipLength := LEN( cBuffer ) + iif( lWithCRLF, 2, 0 )
|
|
::nposition := FSEEK( Self:nDosHandle, - ( LEN( cBuffer ) ), FS_END )
|
|
IF lWithCRLF
|
|
::nposition := FSEEK( Self:nDosHandle, - 2, FS_RELATIVE )
|
|
ENDIF
|
|
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 ISCHARACTER( cChar )
|
|
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, FS_SET )
|
|
Self:nLastDosMessage := FERROR()
|
|
IF Self:noDosError()
|
|
FSEEK( Self:nDosHandle, Self:nPosition, FS_SET ) // Re-position the pointer
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* 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 !( hb_OSNewLine() $ cLine ) // No CRLF, so add
|
|
cLIne += hb_OSNewLine()
|
|
ENDIF
|
|
FSEEK( Self:nDosHandle, 0, FS_END )
|
|
FWRITE( Self:nDosHandle, cLine )
|
|
::nEndOfFile := FSEEK( Self:nDosHandle, 0, FS_END )
|
|
::nposition := FSEEK( Self:nDosHandle, - ( LEN( cLine ) ), FS_END )
|
|
::nSkipLength := LEN( cLine )
|
|
::lAtBottom := ::lAtTop := .F.
|
|
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( .F. )
|
|
::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 := .T. // as logical
|
|
LOCAL cBuffer // as char
|
|
|
|
DEFAULT nValue TO 0
|
|
|
|
IF Self:noDosError() .AND. Self:nDosHandle > 0
|
|
IF ISNUMBER( nValue )
|
|
IF nValue > 0 // o.k. so far
|
|
FSEEK( Self:nDosHandle, 0, FS_SET ) // start at the top
|
|
WHILE lContinue
|
|
cBuffer := SPACE( pBUFFER_LENGTH )
|
|
lContinue := ( FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH ) == ;
|
|
pBUFFER_LENGTH )
|
|
cBuffer := cLine + cBuffer
|
|
WHILE hb_OSNewLine() $ cBuffer
|
|
IF ++ nCount == nValue
|
|
lContinue := .F.
|
|
EXIT
|
|
ENDIF
|
|
cBuffer := SUBSTR( cBuffer, AT( hb_OSNewLine(), cBuffer ) + 2 )
|
|
ENDDO
|
|
cLine := cBuffer
|
|
ENDDO
|
|
IF nCount == nValue // We have a match
|
|
FSEEK( Self:nDosHandle, - ( pBUFFER_LENGTH ), FS_RELATIVE ) // Back off from here
|
|
::nposition := FSEEK( Self:nDosHandle, ;
|
|
( pBUFFER_LENGTH - LEN( cBuffer ) ), ;
|
|
FS_RELATIVE ) // Move
|
|
::nSkipLength := Self:Buffget()
|
|
ELSE
|
|
FSEEK( Self:nDosHandle, ::nposition, FS_SET )
|
|
nCount := 0
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN nCount
|
|
|
|
METHOD BufferGet( lForward ) CLASS FileBase
|
|
|
|
LOCAL cBuffer // as char
|
|
LOCAL nLocation // as int
|
|
LOCAL nRead // as int
|
|
LOCAL lWithCRLF := .F. // as logical
|
|
|
|
DEFAULT lForward TO .T.
|
|
|
|
IF !lForward
|
|
|
|
nRead := FSEEK( Self:nDosHandle, ;
|
|
- ( iif( ::nposition < pBUFFER_LENGTH, ;
|
|
::nposition, ;
|
|
pBUFFER_LENGTH ) ), ;
|
|
FS_RELATIVE ) // rewind backwards
|
|
|
|
cBuffer := SPACE( ::nposition - nRead )
|
|
FREAD( Self:nDosHandle, @cBuffer, ( ::nposition - nRead ) )
|
|
|
|
IF RIGHT( cBuffer, 2 ) == hb_OSNewLine() // with line already
|
|
cBuffer := LEFT( cBuffer, LEN( cBuffer ) - 2 )
|
|
lWithCRLF := .T.
|
|
ENDIF
|
|
nLocation := LEN( cBuffer ) - ( RAT( hb_OSNewLine(), cBuffer ) )
|
|
|
|
ELSE
|
|
cBuffer := SPACE( pBUFFER_LENGTH )
|
|
nRead := FREAD( Self:nDosHandle, @cBuffer, pBUFFER_LENGTH )
|
|
FSEEK( Self:nDosHandle, - ( iif( nRead < pBUFFER_LENGTH, nRead, ;
|
|
pBUFFER_LENGTH ) ), FS_RELATIVE ) // Rewind
|
|
|
|
// Now, parse the string. and file
|
|
|
|
nLocation := AT( hb_OSNewLine(), 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 := .T.
|
|
::lAtTop := .F.
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN nLocation
|
|
|
|
#xtranslate DOSFILENAME(<c>) => substr( <c>, rat("\",<c>)+1 )
|
|
|
|
#define pDOS_HANDLE 1
|
|
#define pDOS_FILE 2
|
|
#define pDOS_PATH 3
|
|
|
|
*+--------------------------------------------------------------------
|
|
*+
|
|
*+ Class FileMan
|
|
*+
|
|
*+--------------------------------------------------------------------
|
|
*+
|
|
CLASS FileMan
|
|
|
|
data aDosHandles // Holds an array of dos handles and names
|
|
|
|
data nHan
|
|
data nLastDosMessage // Holds the DOS error level of last operation
|
|
|
|
METHOD addItem( nDos, cFile, cPath ) // Adds an item to the array of handles
|
|
METHOD delItem( xitem ) // Deletes an item from the array of handles
|
|
|
|
METHOD new() // The constructor for this class
|
|
METHOD closeAll() // Closes all of those files
|
|
METHOD rewindAll() // Positions the file pointer for each file
|
|
METHOD writeAll() // Performs hard-write of all
|
|
|
|
METHOD getFileName( cfile ) // Obtains the name of the file based on ID
|
|
METHOD getFileId( nid ) // Obtains the ID based on file name
|
|
METHOD getFilePath( xItem ) // Obtains file path based on either ID or name
|
|
|
|
METHOD noDosError() // Returns a logical true/false
|
|
|
|
METHOD openfile( cFile, nMethod ) // Opens the specified file and sets error
|
|
METHOD Buffget( ld ) virtual
|
|
|
|
ENDCLASS
|
|
|
|
/* Method: Init/New
|
|
Params: N/A
|
|
Returns: Self
|
|
Purpose: Constructor
|
|
*/
|
|
METHOD new() CLASS FileMan
|
|
|
|
IF ::aDosHandles == NIL // has yet to be initialized
|
|
::aDosHandles := {}
|
|
ENDIF
|
|
|
|
IF ::nLastDosMessage == NIL // has yet to be initialized
|
|
::nLastDosMessage := 0
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
// The following are global operations that need to be performed by all
|
|
// files regardless of their format
|
|
|
|
/* Method: ::closeAll()
|
|
Params: N/A
|
|
Returns: Self
|
|
Purpose: To go through the stack of opened file handles and close each
|
|
file, one at a time. Since this is a global operation, it
|
|
will not check on the status of the error message on each
|
|
pass but will at the start of the evaluation.
|
|
|
|
*/
|
|
METHOD closeAll() CLASS FileMan
|
|
|
|
IF ::nLastDosMessage == 0
|
|
AEVAL( ::aDosHandles, { | aFile | FCLOSE( aFile[ pDOS_HANDLE ] ) } )
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* Method: ::rewindAll()
|
|
Params: N/A
|
|
Returns: Self
|
|
Purpose: To go through the stack of opened file handles and places the
|
|
file pointer to the top of each file, one at a time. Since
|
|
Since this is a global operation, it will not check on the
|
|
status of the error message on each pass but will at the
|
|
start of the evaluation.
|
|
|
|
*/
|
|
METHOD rewindAll() CLASS FileMan
|
|
|
|
IF ::nLastDosMessage == 0
|
|
AEVAL( ::aDosHandles, { | aFile | FSEEK( aFile[ pDOS_HANDLE ], 0, FS_SET ) } )
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* Method: ::writeAll()
|
|
Params: N/A
|
|
Returns: Self
|
|
Purpose: To go through the stack of opened file handles and writes each
|
|
file, one at a time. Since this is a global operation, it
|
|
will not check on the status of the error message on each
|
|
pass but will at the start of the evaluation.
|
|
|
|
*/
|
|
METHOD writeAll() CLASS FileMan
|
|
|
|
IF ::nLastDosMessage == 0
|
|
AEVAL( ::aDosHandles, { | aFile | FWRITE( aFile[ pDOS_HANDLE ], "", 0 ) } )
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* Method: ::getFileName( <nId> )
|
|
Params: <nId> DOS File handle / ID
|
|
Returns: <cName> File name store with that ID handle
|
|
Purpose: This method will return the file's name found
|
|
within the table of this class.
|
|
*/
|
|
METHOD getFileName( nId ) CLASS FileMan // Obtains the name of the file based on ID
|
|
|
|
LOCAL cName := "" // as char
|
|
LOCAL nPosition // as int
|
|
|
|
IF ::nLastDosMessage == 0
|
|
IF ISNUMBER( nId )
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aFile | nId == aFile[ pDOS_HANDLE ] } )
|
|
IF nPosition != 0
|
|
cName := ::aDosHandles[ nPosition, pDOS_FILE ]
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN cName
|
|
|
|
/* Method: ::getFileId( <cName> )
|
|
Params: <cName> File names used to store item to stack
|
|
Returns: <nId> DOS File handle or ID associated with name
|
|
Purpose: This method will return the file's ID or DOS handle found
|
|
within the table of this class.
|
|
*/
|
|
METHOD getFileId( cName ) CLASS FileMan // Obtains the ID based on file name
|
|
|
|
LOCAL nId := 0 // as int
|
|
LOCAL nPosition // as int
|
|
|
|
IF ::nLastDosMessage == 0
|
|
IF ISCHARACTER( cName )
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aFile | cName == aFile[ pDOS_FILE ] } )
|
|
IF nPosition != 0
|
|
nId := ::aDosHandles[ nPosition, pDOS_HANDLE ]
|
|
ENDIF
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN nId
|
|
|
|
/* Method: ::getFilePath( <xItem> )
|
|
Params: <xItem> DOS File handle / ID or stored file name
|
|
Returns: <cPath> Associated file path
|
|
Purpose: This method will return the associated DOS path for either the
|
|
given file name or DOS file handle / ID. If there is no file
|
|
path or if there is an error with the method, the return value
|
|
will be a NULL character byte.
|
|
*/
|
|
METHOD getFilePath( xItem ) CLASS FileMan // Obtains file path based on either ID or name
|
|
|
|
LOCAL cPath := "" // as char
|
|
LOCAL nPosition // as int
|
|
|
|
IF ::nLastDosMessage == 0
|
|
DO CASE
|
|
CASE ISCHARACTER( xItem ) // we've got the file name
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aFile | xItem == aFile[ pDOS_FILE ] } )
|
|
IF nPosition != 0
|
|
cPath := ::aDosHandles[ nPosition, pDOS_PATH ]
|
|
ENDIF
|
|
|
|
CASE ISNUMBER( xItem ) // we've got the file path
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aFile | xItem == aFile[ pDOS_HANDLE ] } )
|
|
IF nPosition != 0
|
|
cPath := ::aDosHandles[ nPosition, pDOS_PATH ]
|
|
ENDIF
|
|
|
|
ENDCASE
|
|
ENDIF
|
|
|
|
RETURN cPath
|
|
|
|
// The following two methods are for the sole purpose of manipulating the
|
|
// array of DOS file handles
|
|
|
|
/* Method: ::addItem( <nDos>, <cFile> [, <cPath] )
|
|
Params: <nDos> DOS file handle
|
|
<cFile> File name
|
|
<cPath> File path, defaults to ""
|
|
Returns: self
|
|
Purpose: This method will add the DOS file ID and name to the internal
|
|
stack. It will not work if either of the the first two
|
|
parameters are not passed to the method OR if the value of
|
|
::nLastDosMessage is 0. The return value of the method will
|
|
be the object itself.
|
|
*/
|
|
METHOD addItem( nDos, cFile, cPath ) CLASS FileMan
|
|
|
|
DEFAULT cPath TO ""
|
|
|
|
IF ::nLastDosMessage == 0
|
|
AADD( ::aDosHandles, { nDos, cFile, cPath } )
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
/* Method: ::delItem( <xItem> )
|
|
Params: <xItem> DOS file handle or file name
|
|
Returns: <lSuccess> Success status of operation
|
|
Purpose: To go through the stack of opened file handles and based on the
|
|
parameter passed to the method, it will remove the file from
|
|
the stack. If <xItem> is a numeric, it will be assumed a
|
|
valud DOS file handle. If <xItem> is character, then it will
|
|
be assumed the name of the file. If <xItem> is neither numeric
|
|
or character or if the value of ::nLastDosMessage is not 0,
|
|
then the method will return a logical false (.F.) value;
|
|
otherwise, a logical true (.T.) will be returned.
|
|
*/
|
|
METHOD delItem( xItem ) CLASS FileMan
|
|
|
|
LOCAL nPosition // as int
|
|
LOCAL lSuccess := .T. // as logical
|
|
|
|
// if xItem is N/Numeric, then seek on first element;
|
|
// if xItem is C/Character, then seek on second element
|
|
|
|
IF ::nLastDosMessage == 0 // No DOS error!
|
|
DO CASE
|
|
CASE ISNUMBER( xItem ) // It's a DOS file handle
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aItem | xItem == aItem[ pDOS_HANDLE ] } )
|
|
IF nPosition == 0
|
|
// Don't remove and set the return value of the function
|
|
lSuccess := .F.
|
|
ELSE
|
|
// Since we have a position, remove from the table and keep the
|
|
// default return value
|
|
ADEL( ::aDosHandles, nPosition )
|
|
ASIZE( ::aDosHandles, LEN( ::aDosHandles ) - 1 )
|
|
ENDIF
|
|
|
|
CASE ISCHARACTER( xItem ) // It's a file name
|
|
nPosition := ASCAN( ::aDosHandles, ;
|
|
{ | aItem | xItem == aItem[ pDOS_FILE ] } )
|
|
IF nPosition == 0
|
|
// Don't remove and set the return value of the function
|
|
lSuccess := .F.
|
|
ELSE
|
|
// Since we have a position, remove from the table and keep the
|
|
// default return value
|
|
ADEL( ::aDosHandles, nPosition )
|
|
ASIZE( ::aDosHandles, LEN( ::aDosHandles ) - 1 )
|
|
ENDIF
|
|
|
|
OTHERWISE
|
|
// Invalid data passed to method
|
|
lSuccess := .F.
|
|
|
|
ENDCASE
|
|
ELSE
|
|
lSuccess := .F.
|
|
|
|
ENDIF
|
|
|
|
RETURN lSuccess
|
|
|
|
/* Method: noDosError()
|
|
Params: N/A
|
|
Returns: <lNoError>
|
|
Purpose: To return a logical true (.T.) if there is no existing error
|
|
state within the system
|
|
*/
|
|
METHOD noDosError() CLASS FileMan
|
|
|
|
RETURN ::nLastDosMessage == 0
|
|
|
|
/* Method: open()
|
|
Params: N/A
|
|
Returns: <nDosHandle>
|
|
Purpose: This method acutally opens the file specified by the parameter
|
|
<cFile> with the open mode of <nMethod>. Each file object
|
|
should carry this information locally and use this method only
|
|
to update the internal table.
|
|
|
|
*/
|
|
METHOD openfile( cFile, nMethod ) CLASS FileMan
|
|
|
|
LOCAL nFileHandle // as int
|
|
LOCAL cFilename // as char
|
|
LOCAL cPath // as char
|
|
|
|
DEFAULT nMethod TO 0
|
|
|
|
nFileHandle := FOPEN( cFile, nMethod ) // opens the file
|
|
IF !EMPTY( FERROR() ) // There was an error in opening
|
|
::nLastDosMessage := FERROR()
|
|
nFileHandle := - 1
|
|
ELSE
|
|
cFileName := DOSFILENAME( cFile )
|
|
cPath := STRTRAN( cFile, cFileName, "" )
|
|
::addItem( nFileHandle, cFileName, cPath )
|
|
ENDIF
|
|
|
|
::nHan := nFileHandle
|
|
|
|
RETURN nFileHandle
|
|
|
|
/*****************************************************************/
|
|
|
|
/****
|
|
* FT_FUSE( cFile, nMode ) ---> nHandle
|
|
* Open a File
|
|
*/
|
|
FUNCTION FT_FUSE( cFile, nMode )
|
|
|
|
IF cFile == NIL
|
|
s_oFileBase:closefile()
|
|
ELSE
|
|
s_oFileBase := FileBase():new( cFile )
|
|
IF nMode != NIL
|
|
s_oFileBase:nOpenMode := nMode
|
|
ENDIF
|
|
s_oFileBase:open()
|
|
ENDIF
|
|
|
|
RETURN s_oFileBase:nHan
|
|
|
|
FUNCTION FT_FEOF()
|
|
RETURN s_oFileBase:lAtBottom
|
|
|
|
FUNCTION FReadLn()
|
|
RETURN s_oFileBase:retrieve()
|
|
|
|
FUNCTION FT_FReadLn()
|
|
RETURN StrTran( FReadLn(), Chr( 13 ), "" )
|
|
|
|
PROCEDURE FT_FGotop()
|
|
s_oFileBase:Gotop()
|
|
RETURN
|
|
|
|
PROCEDURE FT_FSKIP( n )
|
|
s_oFileBase:Skip( n )
|
|
RETURN
|
|
|
|
PROCEDURE FT_MKDIR( cDir )
|
|
|
|
#ifdef HB_COMPAT_C53
|
|
MakeDir( cDir )
|
|
#else
|
|
HB_SYMBOL_UNUSED( cDir ) // TOFIX
|
|
#endif
|
|
|
|
RETURN
|
|
|
|
FUNCTION StrPos( cBuffer )
|
|
|
|
LOCAL x
|
|
LOCAL cChar
|
|
|
|
FOR x := 1 TO LEN( cBuffer )
|
|
|
|
cChar := SUBSTR( cBuffer, x, 1 )
|
|
|
|
IF cChar >= "A" .AND. cChar <= "Z" .OR. ;
|
|
cChar >= "a" .AND. cChar <= "z" .OR. ;
|
|
cChar >= "0" .AND. cChar <= "9" .OR. ;
|
|
cChar $ "@-(|.*#/=?!<>+" .OR. ;
|
|
cChar == '"'
|
|
|
|
RETURN x
|
|
ENDIF
|
|
NEXT
|
|
|
|
RETURN 0
|
|
|
|
FUNCTION GetNumberofTableItems( cBuffer )
|
|
|
|
LOCAL cItem
|
|
LOCAL nItem := 0
|
|
|
|
cBuffer := ALLTRIM( cBuffer )
|
|
|
|
DO WHILE AT( SPACE( 3 ), cBuffer ) > 0
|
|
cItem := SUBSTR( cBuffer, 1, AT( SPACE( 3 ), cBuffer ) - 1 )
|
|
IF AT( SPACE( 3 ), cBuffer ) == 0
|
|
nItem++
|
|
ELSE
|
|
cBuffer := ALLTRIM( STRTRAN( cBuffer, cItem, "" ) )
|
|
nItem++
|
|
ENDIF
|
|
ENDDO
|
|
nItem++
|
|
|
|
RETURN nItem
|
|
|
|
FUNCTION FREADline( nH, cB, nMaxLine )
|
|
|
|
LOCAL cLine := SPACE( nMaxLine )
|
|
LOCAL nSavePos
|
|
LOCAL nEol
|
|
LOCAL nNumRead
|
|
|
|
cB := ""
|
|
nSavePos := FSEEK( nH, 0, FS_RELATIVE )
|
|
nNumRead := FREAD( nH, @cLine, nMaxLine )
|
|
IF ( nEol := AT( hb_OSNewLine(), SUBSTR( cLine, 1, nNumRead ) ) ) == 0
|
|
cB := cLine
|
|
ELSE
|
|
cB := SUBSTR( cLine, 1, nEol - 1 )
|
|
FSEEK( nH, nSavePos + nEol + 1, FS_SET )
|
|
ENDIF
|
|
|
|
RETURN nNumRead != 0
|