Files
harbour-core/harbour/utils/hbdoc/ft_funcs.prg
Viktor Szakats b70cbd6a34 2008-08-22 22:51 UTC+0200 Viktor Szakats (harbour.01 syenar hu)
* utils/hbtest/rt_array.prg
     + Added failing test case of doing AEval() 
       and resizing the evaluated array in the 
       eval codeblock.

   * utils/hbdoc/ft_funcs.prg
   * utils/hbdoc/hbdoc.prg
   * utils/hbmake/ft_funcs.prg
   * utils/hbmake/hbmutils.prg
     ! Two EOL handling fixes applied.
       Thanks to Bill Robertson.
     ; Please test.

   * contrib/xhb/Makefile
   * contrib/xhb/common.mak
   + contrib/xhb/arrayblk.prg
     + Added HB_ARRAYBLOCK()
2008-08-22 20:56:09 +00:00

987 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()
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 + Len( hb_OSNewLine() ) - 1, FS_SET )
ENDIF
RETURN nNumRead != 0