+ tests/stripeol.hb
+ added script to strip spaces at EOL
* contrib/gtalleg/tests/test.prg
* contrib/gtwvg/tests/demoxbp.prg
* contrib/gtwvg/tests/wvgactivex.prg
* contrib/gtwvg/tests/wvgxbp.prg
* contrib/gtwvg/wvgclass.prg
* contrib/hbamf/tests/tstendin.prg
* contrib/hbbz2/tests/test.prg
* contrib/hbcairo/tests/fancytxt.prg
* contrib/hbcairo/tests/table.prg
* contrib/hbct/ctrand.prg
* contrib/hbct/doc/en/addascii.txt
* contrib/hbct/doc/en/atadjust.txt
* contrib/hbct/doc/en/count.txt
* contrib/hbct/doc/en/ct.txt
* contrib/hbct/doc/en/ctc.txt
* contrib/hbct/doc/en/ctmath.txt
* contrib/hbct/doc/en/ctmath2.txt
* contrib/hbct/doc/en/ctpad.txt
* contrib/hbct/doc/en/ctstr.txt
* contrib/hbct/doc/en/datetime.txt
* contrib/hbct/doc/en/dattime2.txt
* contrib/hbct/doc/en/exponent.txt
* contrib/hbct/doc/en/finan.txt
* contrib/hbct/doc/en/ftoc.txt
* contrib/hbct/doc/en/justify.txt
* contrib/hbct/doc/en/keyset.txt
* contrib/hbct/doc/en/misc1.txt
* contrib/hbct/doc/en/num1.txt
* contrib/hbct/doc/en/numat.txt
* contrib/hbct/doc/en/numconv.txt
* contrib/hbct/doc/en/pos1.txt
* contrib/hbct/doc/en/pos2.txt
* contrib/hbct/doc/en/posdiff.txt
* contrib/hbct/doc/en/print.txt
* contrib/hbct/doc/en/range.txt
* contrib/hbct/doc/en/relation.txt
* contrib/hbct/doc/en/remove.txt
* contrib/hbct/doc/en/replace.txt
* contrib/hbct/doc/en/screen1.txt
* contrib/hbct/doc/en/strdiff.txt
* contrib/hbct/doc/en/strswap.txt
* contrib/hbct/doc/en/token1.txt
* contrib/hbct/doc/en/token2.txt
* contrib/hbct/doc/en/trig.txt
* contrib/hbct/doc/en/video.txt
* contrib/hbct/doc/en/wordtoch.txt
* contrib/hbct/getinfo.prg
* contrib/hbfimage/tests/fitest.prg
* contrib/hbgd/gdimage.prg
* contrib/hbgd/tests/counter.prg
* contrib/hbgd/tests/gdtest.prg
* contrib/hbgd/tests/gdtestcl.prg
* contrib/hbgd/tests/test_out.prg
* contrib/hbide/idetags.prg
* contrib/hblzf/tests/test2.prg
* contrib/hbmisc/doc/en/ht_conv.txt
* contrib/hbmisc/fcomma.prg
* contrib/hbmisc/fileread.prg
* contrib/hbmisc/hbedit.prg
* contrib/hbnf/anomatch.prg
* contrib/hbnf/calendar.prg
* contrib/hbnf/clrsel.prg
* contrib/hbnf/dispmsg.prg
* contrib/hbnf/doc/en/aading.txt
* contrib/hbnf/doc/en/aavg.txt
* contrib/hbnf/doc/en/acctadj.txt
* contrib/hbnf/doc/en/acctmnth.txt
* contrib/hbnf/doc/en/acctqtr.txt
* contrib/hbnf/doc/en/acctweek.txt
* contrib/hbnf/doc/en/acctyear.txt
* contrib/hbnf/doc/en/adessort.txt
* contrib/hbnf/doc/en/aemaxlen.txt
* contrib/hbnf/doc/en/aeminlen.txt
* contrib/hbnf/doc/en/amedian.txt
* contrib/hbnf/doc/en/anomatch.txt
* contrib/hbnf/doc/en/any2any.txt
* contrib/hbnf/doc/en/aredit.txt
* contrib/hbnf/doc/en/asum.txt
* contrib/hbnf/doc/en/at2.txt
* contrib/hbnf/doc/en/bitclr.txt
* contrib/hbnf/doc/en/bitset.txt
* contrib/hbnf/doc/en/blink.txt
* contrib/hbnf/doc/en/byt2bit.txt
* contrib/hbnf/doc/en/byt2hex.txt
* contrib/hbnf/doc/en/byteand.txt
* contrib/hbnf/doc/en/byteneg.txt
* contrib/hbnf/doc/en/bytenot.txt
* contrib/hbnf/doc/en/bytexor.txt
* contrib/hbnf/doc/en/calendar.txt
* contrib/hbnf/doc/en/chdir.txt
* contrib/hbnf/doc/en/cntryset.txt
* contrib/hbnf/doc/en/color2n.txt
* contrib/hbnf/doc/en/d2e.txt
* contrib/hbnf/doc/en/datecnfg.txt
* contrib/hbnf/doc/en/dayofyr.txt
* contrib/hbnf/doc/en/daytobow.txt
* contrib/hbnf/doc/en/dfile.txt
* contrib/hbnf/doc/en/diskfunc.txt
* contrib/hbnf/doc/en/dispc.txt
* contrib/hbnf/doc/en/dispmsg.txt
* contrib/hbnf/doc/en/dosver.txt
* contrib/hbnf/doc/en/e2d.txt
* contrib/hbnf/doc/en/easter.txt
* contrib/hbnf/doc/en/elapsed.txt
* contrib/hbnf/doc/en/findith.txt
* contrib/hbnf/doc/en/floptst.txt
* contrib/hbnf/doc/en/ftattr.txt
* contrib/hbnf/doc/en/ftidle.txt
* contrib/hbnf/doc/en/ftisprn.txt
* contrib/hbnf/doc/en/ftround.txt
* contrib/hbnf/doc/en/fttext.txt
* contrib/hbnf/doc/en/gcd.txt
* contrib/hbnf/doc/en/getenvrn.txt
* contrib/hbnf/doc/en/hex2dec.txt
* contrib/hbnf/doc/en/iamidle.txt
* contrib/hbnf/doc/en/isbit.txt
* contrib/hbnf/doc/en/isbiton.txt
* contrib/hbnf/doc/en/isshare.txt
* contrib/hbnf/doc/en/kspeed.txt
* contrib/hbnf/doc/en/linked.txt
* contrib/hbnf/doc/en/madd.txt
* contrib/hbnf/doc/en/menutonf.txt
* contrib/hbnf/doc/en/metaph.txt
* contrib/hbnf/doc/en/miltime.txt
* contrib/hbnf/doc/en/month.txt
* contrib/hbnf/doc/en/netpv.txt
* contrib/hbnf/doc/en/nooccur.txt
* contrib/hbnf/doc/en/ntow.txt
* contrib/hbnf/doc/en/nwlstat.txt
* contrib/hbnf/doc/en/nwsem.txt
* contrib/hbnf/doc/en/nwuid.txt
* contrib/hbnf/doc/en/ontick.txt
* contrib/hbnf/doc/en/origin.txt
* contrib/hbnf/doc/en/page.txt
* contrib/hbnf/doc/en/pchr.txt
* contrib/hbnf/doc/en/peek.txt
* contrib/hbnf/doc/en/pending.txt
* contrib/hbnf/doc/en/poke.txt
* contrib/hbnf/doc/en/popadder.txt
* contrib/hbnf/doc/en/proper.txt
* contrib/hbnf/doc/en/prtesc.txt
* contrib/hbnf/doc/en/putkey.txt
* contrib/hbnf/doc/en/qtr.txt
* contrib/hbnf/doc/en/rand1.txt
* contrib/hbnf/doc/en/savearr.txt
* contrib/hbnf/doc/en/scregion.txt
* contrib/hbnf/doc/en/setdate.txt
* contrib/hbnf/doc/en/setkeys.txt
* contrib/hbnf/doc/en/setlastk.txt
* contrib/hbnf/doc/en/settime.txt
* contrib/hbnf/doc/en/sinkey.txt
* contrib/hbnf/doc/en/sleep.txt
* contrib/hbnf/doc/en/sqzn.txt
* contrib/hbnf/doc/en/sysmem.txt
* contrib/hbnf/doc/en/tbwhile.txt
* contrib/hbnf/doc/en/tempfile.txt
* contrib/hbnf/doc/en/vertmenu.txt
* contrib/hbnf/doc/en/vidcur.txt
* contrib/hbnf/doc/en/vidmode.txt
* contrib/hbnf/doc/en/wda.txt
* contrib/hbnf/doc/en/week.txt
* contrib/hbnf/doc/en/workdays.txt
* contrib/hbnf/doc/en/woy.txt
* contrib/hbnf/doc/en/xbox.txt
* contrib/hbnf/doc/en/year.txt
* contrib/hbnf/ftround.prg
* contrib/hbnf/metaph.prg
* contrib/hbnf/nwsem.prg
* contrib/hbnf/nwuid.prg
* contrib/hbnf/pending.prg
* contrib/hbnf/popadder.prg
* contrib/hbnf/scregion.prg
* contrib/hbnf/tbwhile.prg
* contrib/hbnf/workdays.prg
* contrib/hbodbc/browodbc.prg
* contrib/hbqt/tests/browqt.prg
* contrib/hbqt/tests/dbfbrowserclass.prg
* contrib/hbqt/tests/testbrow.prg
* contrib/hbsqlit3/tests/blob.prg
* contrib/hbsqlit3/tests/sl3_test.prg
* contrib/hbxbp/tests/demoxbp.prg
* contrib/hbxbp/tests/dialogqt.prg
* contrib/hbxbp/xbpdataref.prg
* contrib/hbxbp/xbpmenubar.prg
* contrib/hbxbp/xbpprintdialog.prg
* contrib/hbxbp/xbpprinter.prg
* contrib/hbxbp/xbprtf.prg
* contrib/hbxbp/xbpscrollbar.prg
* contrib/xhb/cstruct.prg
* contrib/xhb/hblog.prg
* contrib/xhb/hterrsys.prg
* contrib/xhb/htjlist.prg
* contrib/xhb/sprintf.prg
* contrib/xhb/stream.prg
* contrib/xhb/tcgi.prg
* contrib/xhb/tedit.prg
* contrib/xhb/tfile.prg
* contrib/xhb/thtm.prg
* contrib/xhb/trpc.prg
* contrib/xhb/trpccli.prg
* contrib/xhb/ttable.prg
* contrib/xhb/txml.prg
* contrib/xhb/xcstr.prg
* contrib/xhb/xhberr.prg
* contrib/xhb/xhbmt.prg
* doc/cmpopt.txt
* doc/en/dbstrux.txt
* examples/hbapollo/array.prg
* examples/hbvpdf/hbvpdf.prg
* examples/hbvpdf/hbvpdft.prg
* examples/hbxlsxml/xlsxml_s.prg
* examples/hbxlsxml/xlsxml_y.prg
* examples/httpsrv/cgifunc.prg
* examples/httpsrv/cookie.prg
* examples/httpsrv/modules/cookie.prg
* examples/httpsrv/modules/info.prg
* examples/httpsrv/modules/showcounter.prg
* examples/httpsrv/modules/tableservletdb.prg
* examples/httpsrv/modules/testajax.prg
* examples/httpsrv/session.prg
* examples/httpsrv/uhttpd.prg
* include/hbclass.ch
* include/hbtrace.ch
* src/debug/dbgtinp.prg
* src/debug/debugger.prg
* src/debug/tbrwtext.prg
* src/rdd/usrrdd/rdds/hscdx.prg
* src/rtl/hbi18n2.prg
* src/rtl/valtoexp.prg
* formatting, fixed indentation, cleanups, deleting system specific information,
optimizing out platform dependent code, using hb_ntos()
242 lines
8.0 KiB
Plaintext
242 lines
8.0 KiB
Plaintext
/*
|
|
* $Id$
|
|
*/
|
|
|
|
/* Harbour Project source code
|
|
* A class that reads a file one line at a time
|
|
http://harbour-project.org/
|
|
Donated to the public domain on 2001-04-03 by David G. Holm <dholm@jsd-llc.com>
|
|
*/
|
|
|
|
#include "hbclass.ch"
|
|
|
|
#include "fileio.ch"
|
|
|
|
#define oF_ERROR_MIN 1
|
|
#define oF_CREATE_OBJECT 1
|
|
#define oF_OPEN_FILE 2
|
|
#define oF_READ_FILE 3
|
|
#define oF_CLOSE_FILE 4
|
|
#define oF_ERROR_MAX 4
|
|
#define oF_DEFAULT_READ_SIZE 4096
|
|
|
|
CREATE CLASS TFileRead
|
|
|
|
VAR cFile // The filename
|
|
VAR nHan // The open file handle
|
|
VAR lEOF // The end of file reached flag
|
|
VAR nError // The current file error code
|
|
VAR nLastOp // The last operation done (for error messages)
|
|
VAR cBuffer // The readahead buffer
|
|
VAR nReadSize // How much to add to the readahead buffer on
|
|
// each read from the file
|
|
|
|
METHOD New( cFile, nSize ) // Create a new class instance
|
|
METHOD Open( nMode ) // Open the file for reading
|
|
METHOD Close() // Close the file when done
|
|
METHOD ReadLine() // Read a line from the file
|
|
METHOD Name() // Retunrs the file name
|
|
METHOD IsOpen() // Returns .T. if file is open
|
|
METHOD MoreToRead() // Returns .T. if more to be read
|
|
METHOD Error() // Returns .T. if error occurred
|
|
METHOD ErrorNo() // Returns current error code
|
|
METHOD ErrorMsg( cText ) // Returns formatted error message
|
|
|
|
PROTECTED:
|
|
|
|
METHOD EOL_pos()
|
|
|
|
END CLASS
|
|
|
|
METHOD New( cFile, nSize ) CLASS TFileRead
|
|
|
|
IF nSize == NIL .OR. nSize < 1
|
|
// The readahead size can be set to as little as 1 byte, or as much as
|
|
// 65535 bytes, but venturing out of bounds forces the default size.
|
|
nSize := oF_DEFAULT_READ_SIZE
|
|
ENDIF
|
|
|
|
::cFile := cFile // Save the file name
|
|
::nHan := -1 // It's not open yet
|
|
::lEOF := .T. // So it must be at EOF
|
|
::nError := 0 // But there haven't been any errors
|
|
::nLastOp := oF_CREATE_OBJECT // Because we just created the class
|
|
::cBuffer := "" // and nothing has been read yet
|
|
::nReadSize := nSize // But will be in this size chunks
|
|
|
|
RETURN Self
|
|
|
|
METHOD Open( nMode ) CLASS TFileRead
|
|
|
|
IF ::nHan == -1
|
|
// Only open the file if it isn't already open.
|
|
IF nMode == NIL
|
|
nMode := FO_READ + FO_SHARED // Default to shared read-only mode
|
|
ENDIF
|
|
::nLastOp := oF_OPEN_FILE
|
|
::nHan := FOPEN( ::cFile, nMode ) // Try to open the file
|
|
IF ::nHan == -1
|
|
::nError := FERROR() // It didn't work
|
|
::lEOF := .T. // So force EOF
|
|
ELSE
|
|
::nError := 0 // It worked
|
|
::lEOF := .F. // So clear EOF
|
|
ENDIF
|
|
ELSE
|
|
// The file is already open, so rewind to the beginning.
|
|
IF FSEEK( ::nHan, 0 ) == 0
|
|
::lEOF := .F. // Definitely not at EOF
|
|
ELSE
|
|
::nError := FERROR() // Save error code if not at BOF
|
|
ENDIF
|
|
::cBuffer := "" // Clear the readahead buffer
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
METHOD ReadLine() CLASS TFileRead
|
|
LOCAL cLine := ""
|
|
LOCAL nPos
|
|
|
|
::nLastOp := oF_READ_FILE
|
|
|
|
IF ::nHan == -1
|
|
::nError := -1 // Set unknown error if file not open
|
|
ELSE
|
|
// Is there a whole line in the readahead buffer?
|
|
nPos := ::EOL_pos()
|
|
WHILE ( nPos <= 0 .OR. nPos > LEN( ::cBuffer ) - 3 ) .AND. !::lEOF
|
|
// Either no or maybe, but there is possibly more to be read.
|
|
// Maybe means that we found either a CR or an LF, but we don't
|
|
// have enough characters to discriminate between the three types
|
|
// of end of line conditions that the class recognizes (see below).
|
|
cLine := FREADSTR( ::nHan, ::nReadSize )
|
|
IF EMPTY( cLine )
|
|
// There was nothing more to be read. Why? (Error or EOF.)
|
|
::nError := FERROR()
|
|
IF ::nError == 0
|
|
// Because the file is at EOF.
|
|
::lEOF := .T.
|
|
ENDIF
|
|
ELSE
|
|
// Add what was read to the readahead buffer.
|
|
::cBuffer += cLine
|
|
ENDIF
|
|
// Is there a whole line in the readahead buffer yet?
|
|
nPos := ::EOL_pos()
|
|
END WHILE
|
|
// Is there a whole line in the readahead buffer?
|
|
IF nPos <= 0
|
|
// No, which means that there is nothing left in the file either, so
|
|
// return the entire buffer contents as the last line in the file.
|
|
cLine := ::cBuffer
|
|
::cBuffer := ""
|
|
ELSE
|
|
// Yes. Is there anything in the line?
|
|
IF nPos > 1
|
|
// Yes, so return the contents.
|
|
cLine := LEFT( ::cBuffer, nPos - 1 )
|
|
ELSE
|
|
// No, so return an empty string.
|
|
cLine := ""
|
|
ENDIF
|
|
// Deal with multiple possible end of line conditions.
|
|
DO CASE
|
|
CASE SUBSTR( ::cBuffer, nPos, 3 ) == CHR( 13 ) + CHR( 13 ) + CHR( 10 )
|
|
// It's a messed up DOS newline (such as that created by a program
|
|
// that uses "\r\n" as newline when writing to a text mode file,
|
|
// which causes the '\n' to expand to "\r\n", giving "\r\r\n").
|
|
nPos += 3
|
|
CASE SUBSTR( ::cBuffer, nPos, 2 ) == CHR( 13 ) + CHR( 10 )
|
|
// It's a standard DOS newline
|
|
nPos += 2
|
|
OTHERWISE
|
|
// It's probably a Mac or Unix newline
|
|
nPos++
|
|
ENDCASE
|
|
::cBuffer := SUBSTR( ::cBuffer, nPos )
|
|
ENDIF
|
|
ENDIF
|
|
|
|
RETURN cLine
|
|
|
|
METHOD EOL_pos() CLASS TFileRead
|
|
LOCAL nCRpos, nLFpos, nPos
|
|
|
|
// Look for both CR and LF in the file read buffer.
|
|
nCRpos := AT( CHR( 13 ), ::cBuffer )
|
|
nLFpos := AT( CHR( 10 ), ::cBuffer )
|
|
DO CASE
|
|
CASE nCRpos == 0
|
|
// If there's no CR, use the LF position.
|
|
nPos := nLFpos
|
|
CASE nLFpos == 0
|
|
// If there's no LF, use the CR position.
|
|
nPos := nCRpos
|
|
OTHERWISE
|
|
// If there's both a CR and an LF, use the position of the first one.
|
|
nPos := MIN( nCRpos, nLFpos )
|
|
ENDCASE
|
|
|
|
RETURN nPos
|
|
|
|
METHOD Close() CLASS TFileRead
|
|
|
|
::nLastOp := oF_CLOSE_FILE
|
|
::lEOF := .T.
|
|
// Is the file already closed.
|
|
IF ::nHan == -1
|
|
// Yes, so indicate an unknown error.
|
|
::nError := -1
|
|
ELSE
|
|
// No, so close it already!
|
|
FCLOSE( ::nHan )
|
|
::nError := FERROR()
|
|
::nHan := -1 // The file is no longer open
|
|
::lEOF := .T. // So force an EOF condition
|
|
ENDIF
|
|
|
|
RETURN Self
|
|
|
|
METHOD Name() CLASS TFileRead
|
|
// Returns the filename associated with this class instance.
|
|
RETURN ::cFile
|
|
|
|
METHOD IsOpen() CLASS TFileRead
|
|
// Returns .T. if the file is open.
|
|
RETURN ::nHan != -1
|
|
|
|
METHOD MoreToRead() CLASS TFileRead
|
|
// Returns .T. if there is more to be read from either the file or the
|
|
// readahead buffer. Only when both are exhausted is there no more to read.
|
|
RETURN !::lEOF .OR. !EMPTY( ::cBuffer )
|
|
|
|
METHOD Error() CLASS TFileRead
|
|
// Returns .T. if an error was recorded.
|
|
RETURN ::nError != 0
|
|
|
|
METHOD ErrorNo() CLASS TFileRead
|
|
// Returns the last error code that was recorded.
|
|
RETURN ::nError
|
|
|
|
METHOD ErrorMsg( cText ) CLASS TFileRead
|
|
STATIC s_cAction := {"on", "creating object for", "opening", "reading from", "closing"}
|
|
|
|
LOCAL cMessage, nTemp
|
|
|
|
// Has an error been recorded?
|
|
IF ::nError == 0
|
|
// No, so report that.
|
|
cMessage := "No errors have been recorded for " + ::cFile
|
|
ELSE
|
|
// Yes, so format a nice error message, while avoiding a bounds error.
|
|
IF ::nLastOp < oF_ERROR_MIN .OR. ::nLastOp > oF_ERROR_MAX
|
|
nTemp := 1
|
|
ELSE
|
|
nTemp := ::nLastOp + 1
|
|
ENDIF
|
|
cMessage := iif( EMPTY( cText ), "", cText ) + "Error " + hb_ntos( ::nError ) + " " + s_cAction[ nTemp ] + " " + ::cFile
|
|
ENDIF
|
|
|
|
RETURN cMessage
|