[Runtime] Increased buffer size for low level file access, fix problems in OrdFor()

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections
USING System.Collections.Generic
USING System.IO
USING System.Linq
USING System.Runtime.InteropServices
USING System.Runtime.CompilerServices
USING System.Security
USING System.Runtime
USING System.Runtime.ConstrainedExecution
USING System.Text

#region Defines
	DEFINE F_ERROR :=  IntPtr{-1} // Error value (all functions)

	// FERROR() returns, which are not reflected as DOSERROR()
	DEFINE FERROR_FULL    := 256   // disk full
	DEFINE FERROR_EOF     := 257   // eof was already reached, when a read was tried
	DEFINE FERROR_PARAM   := 258   // invalid parameter already detected before giving to DOS

	// FSEEK(), _llseek() modes
    /// FSeek() Seek Offset: Seek from beginning of file
	DEFINE FS_SET         := 0
    /// FSeek() Seek Offset: Seek from current file position
	DEFINE FS_RELATIVE    := 1
    /// FSeek() Seek Offset: Seek from end of file
	DEFINE FS_END         := 2

	// FOPEN() access modes
    /// FOpen() Open Mode: Open for reading (default)
	DEFINE FO_READ        := 0
    /// FOpen() Open Mode: Open for writing
	DEFINE FO_WRITE       := 1
    /// FOpen() Open Mode: Open for reading or writing
	DEFINE FO_READWRITE   := 2

	// FOPEN() sharing modes (combine with open mode using +)
    /// FOpen() Sharing Mode: Compatibility mode (default)
	DEFINE FO_COMPAT     := 0x00000000
    /// FOpen() Sharing Mode: Exclusive
	DEFINE FO_EXCLUSIVE  := 0x00000010
    /// FOpen() Sharing Mode: Prevent other processes from writing
	DEFINE FO_DENYWRITE  := 0x00000020
    /// FOpen() Sharing Mode: Prevent other processes from reading
	DEFINE FO_DENYREAD   := 0x00000030
    /// FOpen() Sharing Mode: (same as FO_SHARED)
	DEFINE FO_DENYNONE   := 0x00000040
    /// FOpen() Sharing Mode:  Allow other processes to read or write
	DEFINE FO_SHARED     := 0x00000040  //

	DEFINE OF_SHARE_COMPAT		:= 0x00000000
	DEFINE OF_SHARE_EXCLUSIVE	:= 0x00000010
	DEFINE OF_SHARE_DENY_WRITE	:= 0x00000020
	DEFINE OF_SHARE_DENY_READ	:= 0x00000030
	DEFINE OF_SHARE_DENY_NONE	:= 0x00000040
	//DEFINE OF_PARSE				:= 0x00000100

	DEFINE CREATE_NEW := 1
	DEFINE CREATE_ALWAYS := 2
	DEFINE OPEN_EXISTING := 3
	DEFINE OPEN_ALWAYS := 4
	DEFINE FO_DELETE := 0x00000200
	DEFINE FO_VERIFY := 0x00000400
	DEFINE FO_CANCEL := 0x00000800
	DEFINE FO_CREATE := 0x00001000
	DEFINE FO_PROMPT := 0x00002000
	DEFINE FO_EXIST  := 0x00004000
	DEFINE FO_REOPEN := 0x00008000

	// FXOPEN() mode
	DEFINE FXO_WILD      := 0x00010000  // Allow wildcards in file name

	// FCREATE() file attribute modes (always opens with OF_READWRITE)
    /// FCreate() attribute mode: normal read/write file (default for create)
	DEFINE FC_NORMAL     := 0x00000000
    /// FCreate() attribute mode: read-only file
	DEFINE FC_READONLY   := 0x00000001
    /// FCreate() attribute mode: hidden file
	DEFINE FC_HIDDEN     := 0x00000002
    /// FCreate() attribute mode: system file
	DEFINE FC_SYSTEM     := 0x00000004
    /// FCreate() attribute mode: archived
	DEFINE FC_ARCHIVED   := 0x00000020

	//
	// additional file attribute for DIRECTORY(), FFIRST() and FFCOUNT()
	//
	DEFINE FA_VOLUME     := 0x00000008
	DEFINE FA_DIRECTORY  := 0x00000010
	DEFINE FA_NORMAL	 := 0x00000080
	DEFINE FA_TEMPORARY  := 0x00000100
	DEFINE FA_COMPRESSED := 0x00000800
	DEFINE FA_OFFLINE    := 0x00001000

#endregion

BEGIN NAMESPACE XSharp.IO

	INTERNAL CLASS VOFileMode
		PROPERTY lWild			AS LOGIC AUTO
		PROPERTY FileMode		AS FileMode AUTO
		PROPERTY FileAccess		AS FileAccess AUTO
		PROPERTY Attributes	    AS DWORD AUTO
		PROPERTY FileShare		AS FileShare AUTO

		CONSTRUCTOR(dwMode AS DWORD, dwAttribs AS DWORD)
			// Wildcard
			IF (DWORD)_AND(dwMode, FXO_WILD) == FXO_WILD
				lWild := TRUE
				dwMode := dwMode - FXO_WILD
			ELSE
				lWild := FALSE
			ENDIF
			Attributes := dwAttribs

			// Access
			fileAccess	:= FileAccess.read
			IF (DWORD)_AND(dwMode,FO_READWRITE) == FO_READWRITE
				fileAccess	:= FileAccess.readWrite
			ELSEIF (DWORD)_AND(dwMode,FO_WRITE) == FO_WRITE
				fileAccess	:= FileAccess.write
			ENDIF
			// Create
			fileMode := FileMode.Open
			FileShare  := FileShare.readWrite
			IF _AND(dwMode,FO_CREATE) == FO_CREATE
				fileMode	:= FileMode.Create
				fileAccess	:= FileAccess.readWrite
                fileShare   := FileShare.None
                RETURN
			ENDIF

			LOCAL dwTempMode AS DWORD
			dwTempMode := (DWORD)_OR(OF_SHARE_DENY_WRITE, OF_SHARE_DENY_READ, OF_SHARE_DENY_NONE)
			dwTempMode := (DWORD)_AND(dwMode,dwTempMode)

			SWITCH dwTempMode
				CASE FO_DENYNONE
					FileShare  := FileShare.readWrite

				CASE FO_DENYWRITE
					FileShare  := FileShare.read

				CASE FO_DENYREAD
					FileShare  := FileShare.write

				CASE FO_EXCLUSIVE
					FileShare  := FileShare.None

				CASE FO_COMPAT
					FileShare  := FileShare.readWrite

			END SWITCH
			RETURN

	END CLASS

	INTERNAL CLASS FileCacheElement
		PUBLIC PROPERTY Stream	   AS Stream AUTO
		PUBLIC PROPERTY Attributes AS DWORD AUTO
		PUBLIC PROPERTY Bytes	   AS BYTE[] AUTO

		CONSTRUCTOR (oStream AS Stream, dwAttributes AS DWORD)
			SELF:Attributes := dwAttributes
			SELF:Stream     := oStream
			RETURN
	END CLASS
	/// <exclude />
	INTERNAL STATIC CLASS File
        /// <exclude />
        PUBLIC STATIC errorCode	:= 0 AS DWORD
        PUBLIC STATIC lastException := NULL AS Exception
        /// <exclude />
		PUBLIC STATIC LastFound AS STRING
		PRIVATE STATIC streams AS Dictionary<IntPtr, FileCacheElement >
		PRIVATE STATIC random AS Random

	    /// <exclude />
        STATIC CONSTRUCTOR
			streams := Dictionary<IntPtr, FileCacheElement >{}
			random := Random{}

		STATIC INTERNAL METHOD findStream(pStream AS IntPtr) AS Stream
            LOCAL element := NULL AS FileCacheElement
            IF streams:TryGetValue(pStream, OUT element)
                RETURN element:stream
            ENDIF
			RETURN NULL

		STATIC PRIVATE METHOD hasStream(pStream AS Intptr) AS LOGIC
			RETURN streams:ContainsKey(pStream)

		STATIC PRIVATE METHOD addStream(pStream AS Intptr, oStream AS Stream, attributes AS DWORD) AS LOGIC
			IF ! streams:ContainsKey(pStream)
				streams:Add(pStream, FileCacheElement {oStream, attributes})
				RETURN TRUE
			ENDIF
			RETURN FALSE

		STATIC INTERNAL METHOD setStream(pStream AS IntPtr, oStream AS Stream) AS LOGIC
            LOCAL element := NULL AS FileCacheElement
            IF streams:TryGetValue(pStream, OUT element)
                element:Stream := oStream
                RETURN TRUE
            ENDIF
			RETURN FALSE

		STATIC PRIVATE METHOD removeStream(pStream AS Intptr) AS LOGIC

			IF streams:ContainsKey(pStream)
				streams:Remove(pStream)
				RETURN TRUE
			ENDIF
			RETURN FALSE

		STATIC PRIVATE METHOD createManagedFileStream(cFIle AS STRING, oMode AS VOFileMode) AS FileStream
			LOCAL oStream := NULL AS FileSTream
			TRY
                clearErrorState()
				oStream := FileStream{cFile, oMode:FileMode, oMode:FileAccess, oMode:FileShare, 16*1024}

			CATCH e AS Exception
				System.Diagnostics.Trace.writeLine(e:Message)
				setErrorState(e)
			END TRY
			RETURN oStream

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        STATIC METHOD ClearErrorState() AS VOID
            lastException := NULL
            errorCode := 0
            RETURN

		STATIC METHOD SetErrorState ( o AS Exception ) AS VOID
            LOCAL e AS Error
            e := Error{o}
            e:StackTrace := o:StackTrace+Environment.NewLine+System.Diagnostics.StackTrace{1,TRUE}:ToString()
            lastException := e
            errorCode := _AND ( (DWORD) System.Runtime.InteropServices.Marshal.GetHRForException ( o ) , 0x0000FFFF )
            e:OsCode := errorCode
            RETURN

		STATIC INTERNAL METHOD CreateFile(cFIle AS STRING, oMode AS VOFileMode) AS IntPtr
			LOCAL hFile := F_ERROR AS IntPtr
			LOCAL oStream AS FileStream
			IF System.IO.File.Exists(cFile) .AND. oMode:FileMode == FileMode.Create
				VAR fi := FileInfo{cFile}
                IF ! fi:IsReadOnly
				    fi:Attributes := FileAttributes.Normal
                ENDIF
			ENDIF

			oStream := createManagedFileStream(cFile, oMode)
			IF oStream != NULL
				hFile := (IntPtr) random:Next(1, Int32.MaxValue)
				DO WHILE streams:ContainsKey(hFile)
					hFile := (IntPtr) random:Next(1, Int32.MaxValue)
				ENDDO
			ELSE
				hFile := F_ERROR
			ENDIF
			//endif
			IF hFile != F_ERROR
				addStream(hFile, oStream, oMode:Attributes)
			ENDIF
			RETURN hFile

		STATIC INTERNAL METHOD GetBuffer(pStream AS IntPtr, nSize AS INT) AS BYTE[]
			IF hasStream(pStream)
				LOCAL element := streams[pStream] AS FileCacheElement
				IF element:Bytes == NULL .OR. element:Bytes:Length < nSize
					element:Bytes := BYTE[]{nSize}
				ENDIF
				RETURN element:Bytes
			ENDIF
			RETURN NULL

		STATIC INTERNAL METHOD close(pStream AS IntPtr) AS LOGIC
			IF hasStream(pStream)
				LOCAL oStream      := streams[pStream]:Stream AS Stream
				LOCAL dwAttributes := streams[pStream]:Attributes AS DWORD
				removeStream(pStream)
                TRY
                    clearErrorState()
				    oStream:Flush()
				    oStream:Close()
				    IF dwAttributes != 0 .AND. oStream IS FileStream VAR oFileStream
					    VAR fi := FileInfo{oFileStream:Name}
					    fi:Attributes := (FileAttributes) dwAttributes
				    ENDIF
				    oStream:Dispose()
				RETURN TRUE
                CATCH e AS Exception
                    setErrorState(e)
                END TRY
			ENDIF
			RETURN FALSE

		INTERNAL STATIC METHOD read(pFile AS Intptr, refC OUT STRING, iCount AS INT, lOem2Ansi AS LOGIC) AS INT
			LOCAL aBuffer := XSharp.IO.File.getBuffer(pFile, iCount) AS BYTE[]
			iCount := (INT) ReadBuff(pFile, aBuffer, iCount, lOem2Ansi)
			refC := Bytes2String(aBuffer, iCount)
			RETURN iCount

		INTERNAL STATIC METHOD readBuff(pFile AS IntPtr,pBuffer AS BYTE[],dwCount AS INT) AS INT64
			LOCAL iResult := 0 AS INT64
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					iResult := oStream:Read(pBuffer,0,(INT) dwCount)
				CATCH e AS Exception
					setErrorState(e)
				END TRY
			ENDIF
			RETURN iResult

		INTERNAL STATIC METHOD readBuff(pFile AS IntPtr,pBuffer AS BYTE[],dwCount AS INT, lAnsi AS LOGIC) AS INT64
            LOCAL iResult := 0 AS INT64
            iResult := readBuff(pFile, pBuffer, dwCount)
            IF FError() == 0 .AND. !lAnsi
				Oem2AnsiA(pBuffer)
			ENDIF
			RETURN iResult

		INTERNAL STATIC METHOD readLine(pFile AS IntPtr,iCount AS INT) AS STRING
			LOCAL cResult := "" AS STRING
			LOCAL nPos	AS INT64
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			IF iCount <= 0
				// According to the VO docs the default value for the buffer length = 256
				iCount := 256
			ENDIF
			IF oStream != NULL_OBJECT
				TRY
					LOCAL pBuffer := XSharp.IO.File.getBuffer(pFile, iCount) AS BYTE[]
                    clearErrorState()
					nPos    := oStream:Position
					iCount := oStream:Read(pBuffer,0,(INT) iCount)
					cResult := Bytes2Line(pBuffer, REF iCount)
					nPos += iCount	// advance CRLF
					oStream:Position := nPos
				CATCH e AS Exception
					setErrorState(e)
				END TRY
			ENDIF
			RETURN cResult

		INTERNAL STATIC METHOD seek(pFile AS IntPtr,lOffset AS INT64,dwOrigin AS DWORD) AS INT64
			LOCAL oStream AS Stream
			LOCAL iResult AS INT64
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				iResult := -1

				TRY
                    clearErrorState()
					SWITCH dwOrigin
						CASE FS_END
							iResult := oStream:Seek(lOffSet, SeekOrigin.End)
						CASE FS_RELATIVE
							iResult := oStream:Seek(lOffSet, SeekOrigin.Current)
						CASE FS_SET
							iResult := oStream:Seek(lOffSet, SeekOrigin.Begin)
						OTHERWISE
							iResult := -1
					END SWITCH
				CATCH e AS Exception
					setErrorState(e)
				END TRY
				RETURN iResult
			ENDIF
			RETURN -1

		INTERNAL STATIC METHOD write( pFile AS IntPtr, c AS STRING, nLength AS INT, lAnsi AS LOGIC ) AS INT
			LOCAL aBytes := String2Bytes(c) AS BYTE[]
			RETURN writeBuff(pFile, aBytes, nLength, lAnsi)

		INTERNAL STATIC METHOD writeBuff(pFile AS IntPtr,pBuffer AS BYTE[],iCount AS INT) AS INT
			LOCAL oStream	AS Stream
			LOCAL iWritten := 0 AS INT
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					oStream:Write(pBuffer, 0, iCount)
					iWritten := iCount
				CATCH e AS Exception
					setErrorState(e)
				END TRY

			ENDIF

			RETURN iWritten

		INTERNAL STATIC METHOD writeBuff(pFile AS IntPtr,pBuffer AS BYTE[],iCount AS INT, lAnsi AS LOGIC) AS INT
			IF !lAnsi
				pBuffer := Ansi2Oem(pBuffer)
            ENDIF
            RETURN writeBuff(pFile, pBuffer, iCount)

		INTERNAL STATIC METHOD writeLine(pFile AS IntPtr,c AS STRING, nLen AS INT) AS INT
			IF c:Length > nLen
				c := c:Substring(0, nLen)
			ENDIF
			RETURN Write(pFile, c + e"\r\n", nLen+2, TRUE)

		INTERNAL STATIC METHOD lock(pFile AS IntPtr,iOffset AS INT64,iLength AS INT64, lLock AS LOGIC) AS LOGIC
			LOCAL oStream := findStream(pFile) AS Stream
			LOCAL lResult := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
                    IF oStream IS FileStream VAR oFileStream
					    IF (lLock)
						    oFileStream:Lock(iOffset, iLength)
					    ELSE
						    oFileStream:UnLock(iOffset, iLength)
                        ENDIF
                        ENDIF
					lResult := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lResult := FALSE
				END TRY
			ENDIF
			RETURN lResult

		INTERNAL STATIC METHOD flush(pFile AS IntPtr, lCommit AS LOGIC) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lOk := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					IF lCommit .AND. oStream IS FileStream VAR oFileStream
						oFileStream:Flush(TRUE)
					ELSE
						oStream:Flush()
					ENDIF
					lOk := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lOk := FALSE
				END TRY
			ENDIF
			RETURN lOk
		INTERNAL STATIC METHOD ChSize(pFile AS IntPtr,nValue AS DWORD) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lOk := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					oStream:SetLength(nValue)
					lOk := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lOk := FALSE
				END TRY
			ENDIF
			RETURN lOk

		INTERNAL STATIC METHOD Eof(pFile AS IntPtr) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lResult := TRUE AS LOGIC
			IF oStream != NULL_OBJECT
				lResult := oStream:Position == oStream:Length
			ENDIF
			RETURN lResult

		INTERNAL STATIC METHOD Tell(pFile AS IntPtr) AS INT64
			LOCAL oStream AS Stream
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					RETURN oStream:Position
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
				END TRY
			ENDIF
			RETURN -1

        INTERNAL STATIC  METHOD ConvertToMemoryStream(pFile AS IntPtr) AS VOID
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
                  LOCAL oNewStream AS Stream
                 IF oStream IS XsMemoryStream
                        RETURN
                 ENDIF
                 IF oStream:Length >= Int32.MaxValue
                      THROW Error{"Cannot convert stream because input stream is too large: "+oStream:Length:ToString()}
                 ELSE
              	    oNewStream := XsMemoryStream{oStream}
                 ENDIF
                 XSharp.IO.File.setStream(pFile, oNewStream)
                 RETURN
            ENDIF
			RETURN

        INTERNAL STATIC  METHOD ConvertToFileStream(pFile AS IntPtr) AS VOID
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream IS XsMemoryStream VAR oMemoryStream
                 oMemoryStream:Save()
                 XSharp.IO.File.setStream(pFile, oMemoryStream:FileStream)
                 RETURN
            ENDIF
            THROW Error{"Could not convert stream, source stream is not a Memory Stream"}
			RETURN

	END CLASS

	INTERNAL CLASS XsMemoryStream INHERIT MemoryStream
        INTERNAL FileStream AS Stream
        PRIVATE written AS LOGIC
        CONSTRUCTOR (oFileStream AS Stream)
            SUPER((INT) oFileStream:Length)
            SELF:FileStream := oFileStream
            VAR nPos := SELF:FileStream:Position
            SELF:FileStream:Flush()
            SELF:FileStream:Position := 0
            SELF:FileStream:CopyTo(SELF)
            SELF:FileStream:Position := nPos
            SELF:Position := nPos
            written := FALSE
            RETURN
         INTERNAL METHOD Save() AS VOID
            VAR nPos := SELF:Position
            IF SELF:written
                SELF:Flush()
                SELF:FileStream:SetLength(SELF:Length)
                SELF:FileStream:Position := 0
                SELF:Position := 0
                SELF:CopyTo(SELF:FileStream)
                SELF:FileStream:Position := nPos
                SELF:Position := nPos
            ENDIF
            RETURN

         OVERRIDE METHOD Write(buffer AS BYTE[], offset AS INT, count AS INT) AS VOID
             SUPER:Write(buffer, offset, count)
             written := TRUE

         OVERRIDE METHOD WriteByte(aByte AS BYTE) AS VOID
             SUPER:WriteByte(aByte)
             written := TRUE

         OVERRIDE METHOD SetLength(len AS INT64) AS VOID
             SUPER:SetLength(len)
             written := TRUE

    END CLASS
END NAMESPACE

///
/// Set the error code for a file operation.
///
/// <param name="nSet"></param>
/// <returns>The previous errorcode from the last file operation.</returns>
FUNCTION FError(nErrorCode AS DWORD) AS DWORD
	LOCAL nOldError AS DWORD
	nOldError := XSharp.IO.File.errorCode
	XSharp.IO.File.errorCode := nErrorCode
	RETURN nOldError

///
/// Get the error code for a file operation.
///
/// <returns>The error from the last file operation or the last user-specified setting.  If there was no error, FError() returns 0.</returns>
FUNCTION FError() AS DWORD
	LOCAL nOldError AS DWORD
	nOldError := XSharp.IO.File.errorCode
	RETURN nOldError

///
/// Get the last exception for a file operation.
///
/// <returns>The exception from the last file operation or the last user-specified setting.  If there was no exception, FException() returns null.</returns>
FUNCTION FException() AS Exception
	RETURN XSharp.IO.File.lastException

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fchsize/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FChSize(ptrHandle AS IntPtr,dwOffset AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.Chsize(ptrHandle, dwOffset)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fclose/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FClose(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.Close(ptrHandle)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcommit/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FCommit(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.flush(ptrHandle, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/feof/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FEof(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.eof(ptrHandle)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fflock/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FFLock(ptrHandle AS IntPtr,dwOffset AS DWORD,dwLength AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.lock(ptrHandle, (INT64) dwOffset, (INT64) dwLength, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fflush/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FFlush(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.flush(ptrHandle, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/ffunlock/*" />
/// <inheritdoc cref="M:XSharp.Core.Functions.FFLock(System.IntPtr,System.UInt32,System.UInt32)" />"
FUNCTION FFUnLock(ptrHandle AS IntPtr,dwOffset AS DWORD,dwLength AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.lock(ptrHandle, (INT64) dwOffset, (INT64) dwLength, FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets/*" />
FUNCTION FGetS(ptrHandle AS IntPtr) AS STRING
	// According to the VO docs the dedault value for the buffer length = 256
	RETURN XSharp.IO.File.readLine(ptrHandle,256)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets/*" />
FUNCTION FGetS(ptrHandle AS IntPtr,nMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) nMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets2/*" />
FUNCTION FGetS2(ptrHandle AS IntPtr,dwMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) dwMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs/*" />
FUNCTION FPutS(ptrHandle AS IntPtr,cBuffer AS STRING) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, (DWORD) cBuffer:Length)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs/*" />
FUNCTION FPutS(ptrHandle AS IntPtr,cBuffer AS STRING,nBytes AS DWORD) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, nBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs3/*" />
FUNCTION FPutS3(ptrHandle AS IntPtr,cBuffer AS STRING, dwBytes AS DWORD) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread3/*" />
/// <param name="ptrBufferVar">An array of bytes to store the data read from the specified file. The length of this variable must be greater than or equal to the number of bytes in the next parameter.</param>
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FRead3(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread4/*" />
/// <param name="ptrBufferVar">An array of bytes to store the data read from the specified file. The length of this variable must be greater than or equal to the number of bytes in the next parameter.</param>
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FRead4(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD,lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes, lAnsi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadline/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadLine(ptrHandle AS IntPtr,nMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) nMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadline2/*" />
FUNCTION FReadLine2(ptrHandle AS IntPtr,dwMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) dwMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadstr/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadStr(ptrHandle AS IntPtr,dwBytes AS DWORD) AS STRING
	LOCAL cResult AS STRING
	XSharp.IO.File.read(ptrHandle, OUT cResult, (INT) dwBytes, XSharp.RuntimeState.Ansi)
	RETURN cResult

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadtext/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadText(ptrHandle AS IntPtr,cBufferVar REF STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.read(ptrHandle, OUT cBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread/*" />
FUNCTION FRead(ptrHandle AS IntPtr,cBufferVar REF STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.read(ptrHandle, OUT cBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadtext3/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadText3(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/frewind/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FRewind(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.Seek(ptrHandle, 0, FS_SET) == 0

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fseek3/*" />
/// <remarks>
/// The possible values for the origin are:
/// <list type="table">
/// <listheader>
/// <term>Constant</term>           <description>Seeks from</description>
/// </listheader>
///	<item><term>FS_END</term>       <description>End-of-file   </description></item>
/// <item><term>FS_RELATIVE</term>  <description>Current pointer position  </description></item>
/// <item><term>FS_SET</term>       <description>Beginning-of-file </description></item>
/// </list>
/// </remarks>
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FSeek3(ptrHandle AS IntPtr,liOffset AS LONG, dwOrigin AS DWORD) AS LONG
	RETURN (LONG) XSharp.IO.File.Seek(ptrHandle, (INT64) liOffset, dwOrigin)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/ftell/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FTell(ptrHandle AS IntPtr) AS DWORD
	RETURN (DWORD) XSharp.IO.File.Tell(ptrHandle)

/// <inheritdoc cref="M:XSharp.Core.Functions.FTell(System.IntPtr)" />
FUNCTION FTell64(pFile AS IntPtr) AS INT64
	RETURN XSharp.IO.File.Tell(pFile)

/// <inheritdoc cref="M:XSharp.Core.Functions.FWrite(System.IntPtr,System.String,System.UInt32)" />
FUNCTION FWrite( pFile AS IntPtr, c AS STRING ) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write( pFile, c,  c:Length, TRUE )

/// <overloads>
///
/// Write a string to an open file
///
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FWrite( ptrHandle AS IntPtr, cBuffer AS STRING, nBytes AS DWORD ) AS DWORD
	RETURN FWrite( ptrHandle, cBuffer, nBytes, TRUE)

/// <inheritdoc cref="M:XSharp.Core.Functions.FWrite(System.IntPtr,System.String,System.UInt32)" />
/// <param name="lAnsi">If FALSE an OEM to ANSI conversion is made. </param>
FUNCTION FWrite( ptrHandle AS IntPtr, cBuffer AS STRING, nBytes AS DWORD, lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write(ptrHandle, cBuffer, (INT) nBytes, lAnsi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite3/*" />
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FWrite3(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle, ptrBuffer, (INT) dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite4/*" />
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FWrite4(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD,lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle,ptrBuffer , (INT) dwBytes ,lAnsi )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline/*" />
FUNCTION FWriteLine(ptrHandle AS IntPtr,cBuffer AS STRING) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write(ptrHandle, cBuffer + e"\r\n",cBuffer:Length+2, TRUE)

/// <overloads>
///
/// Write a string, a carriage-return character, and a linefeed character to an open file.
///
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FWriteLine(ptrHandle AS IntPtr,cBuffer AS STRING,nBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeLine(ptrHandle, cBuffer, (INT) nBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline3/*" />
FUNCTION FWriteLine3(ptrHandle AS IntPtr,cBuffer AS STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeLine(ptrHandle, cBuffer, (INT) dwBytes )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwritetext3/*" />
FUNCTION FWriteText3(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle ,ptrBuffer ,(INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/adjustfname/*" />
FUNCTION AdjustFName(cFileName AS STRING) AS STRING
	LOCAL adjusted := NULL AS STRING
	IF ( !string.IsNullOrEmpty(cFileName) )
		adjusted := System.IO.Path.GetFileNameWithoutExtension(cFileName):TrimEnd()
		IF ( cFileName:IndexOf('.') > 0 )
			adjusted += System.IO.Path.GetExtension(cFileName)
		ENDIF
	ENDIF
	RETURN adjusted

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/adjustfnamea/*" />
FUNCTION AdjustFNameA(cFileName REF STRING) AS STRING
	cFileName := AdjustFName(cFileName)
	RETURN cFileName

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate2/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FCreate2(cFileName AS STRING,dwAttributes AS DWORD) AS IntPtr
	LOCAL oFileMode AS VOFileMode
	oFileMode := VOFileMode{ FO_CREATE, dwAttributes }
	RETURN XSharp.IO.File.CreateFile(cFileName, oFileMode)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate/*" />
FUNCTION FCreate(cFileName AS STRING ) AS IntPtr
	RETURN FCreate2(cFileName, _OR(FC_NORMAL, FO_EXCLUSIVE))

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate/*" />
FUNCTION FCreate(cFileName AS STRING ,kAttributes AS DWORD) AS IntPtr
	RETURN FCreate2(cFileName, kAttributes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen/*" />
FUNCTION FOpen(cFileName AS STRING) AS IntPtr
	RETURN FOpen2(cFileName, FC_NORMAL)

/// <overloads>
/// Open a file.
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen/*" />
/// <include file="CoreComments.xml" path="Comments/FileHandle/*" />

/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FOpen(cFileName AS STRING,kMode AS DWORD) AS IntPtr
	RETURN FOpen2(cFileName, kMode)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen2/*" />
FUNCTION FOpen2(cFileName AS STRING,kMode AS DWORD) AS IntPtr
	LOCAL oFileMode AS VOFileMode
	oFileMode := VOFileMode{kMode, 0}
	RETURN XSharp.IO.File.CreateFile(cFileName, oFileMode)

FUNCTION FConvertToMemoryStream(pFile AS IntPtr) AS IntPtr
   TRY
        XSharp.IO.File.ClearErrorState()
        XSharp.IO.File.ConvertToMemoryStream(pFile)
    CATCH e AS Exception
        XSharp.IO.File.setErrorState(e)
        RETURN F_ERROR
   END TRY
   RETURN pFile

FUNCTION FConvertToFileSTream(pFile AS IntPtr) AS IntPtr
   TRY
        XSharp.IO.File.ClearErrorState()
        XSharp.IO.File.ConvertToFileStream(pFile)
    CATCH e AS Exception
        XSharp.IO.File.setErrorState(e)
        RETURN F_ERROR
   END TRY
   RETURN pFile

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfattr/*" />
FUNCTION GetFAttr(uAttributes AS STRING) AS DWORD
	RETURN String2FAttr(uAttributes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfattr/*" />
FUNCTION GetFAttr(uAttributes AS DWORD) AS DWORD
	RETURN uAttributes

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfmask/*" />
FUNCTION GetFMask(cString AS STRING) AS STRING
	LOCAL cResult AS STRING
	IF String.IsNullOrEmpty(cString)
		cResult := "*.*"
	ELSE
		VAR cChar := cString[cString:Length-1]
		SWITCH cChar
		CASE ':'
		CASE '\\'
			cResult := cString + "*.*"
		OTHERWISE
			cResult := cString
		END SWITCH
	ENDIF
	RETURN cResult

/// <param name="cAttr"> One or more of the following constants or strings: ADHSRV</param>
/// <returns>
/// </returns>
FUNCTION String2FAttr(cAttr AS STRING) AS DWORD
	LOCAL dwAttributes := FC_NORMAL AS DWORD
	IF !String.IsNullOrEmpty(cAttr)
		FOREACH c AS CHAR IN cAttr
			SWITCH Char.ToUpper(c)
				CASE 'A'
					dwAttributes |= FC_ARCHIVED
				CASE 'C'
					dwAttributes |= FA_COMPRESSED
				CASE 'D'
					dwAttributes |= FA_DIRECTORY
				CASE 'H'
					dwAttributes |= FC_HIDDEN
				CASE 'R'
					dwAttributes |= FC_READONLY
				CASE 'S'
					dwAttributes |= FC_SYSTEM
				CASE 'V'
					dwAttributes |= FA_VOLUME
			END SWITCH
		NEXT
	ENDIF
	RETURN dwAttributes

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fattr2string/*" />
FUNCTION FAttr2String(dwAttributes AS DWORD) AS STRING
	LOCAL cAttribute := "" AS STRING

	IF (DWORD)_AND(dwAttributes,FA_DIRECTORY) == FA_DIRECTORY
		cAttribute := cAttribute + "D"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FA_VOLUME) == FA_VOLUME
		cAttribute := cAttribute + "V"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_ARCHIVED) == FC_ARCHIVED
		cAttribute := cAttribute + "A"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_HIDDEN) == FC_HIDDEN
		cAttribute := cAttribute + "H"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_READONLY) == FC_READONLY
		cAttribute := cAttribute + "R"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_SYSTEM) == FC_SYSTEM
		cAttribute := cAttribute + "S"
	ENDIF

	RETURN cAttribute

/// <exclude/>
FUNCTION Bytes2String(aBytes AS BYTE[], nBuffLen AS INT) AS STRING
	LOCAL aChars := CHAR[]{nBuffLen} AS CHAR[]
	LOCAL encoding := StringHelpers.WinEncoding AS Encoding
	encoding:GetChars(aBytes, 0, nBuffLen, aChars, 0)
	RETURN STRING{aChars, 0, nBuffLen}

/// <exclude/>
FUNCTION String2Bytes(sSource AS STRING) AS BYTE[]
	LOCAL ret AS BYTE[]
	IF sSource != NULL
		LOCAL encoding := StringHelpers.WinEncoding AS Encoding
		ret := encoding:GetBytes( sSource )
	ELSE
		ret := BYTE[]{0}
	ENDIF
	RETURN ret

INTERNAL FUNCTION Bytes2Line(aBytes AS BYTE[], nBuffLen REF INT) AS STRING
	// determine end of line
	// note that VO looks for CR LF. CR alone or LF alone to not count as end of line
	LOCAL nLF AS INT
	LOCAL lFoundCR := FALSE AS LOGIC
	LOCAL lFoundCRLF := FALSE AS LOGIC
	nLF := nBuffLen
	FOR VAR nI := 0 TO nBuffLen-1
		SWITCH aBytes[nI]
			CASE 10
				IF lFoundCR		// immediately LF after CR
					nLF := nI -1
					lFoundCRLF := TRUE
				ENDIF
			CASE 13
				lFoundCR := TRUE
			OTHERWISE
				lFoundCR := FALSE
		END SWITCH
		IF lFoundCRLF
			EXIT
		ENDIF
	NEXT
	// when we have found a CRLF then nLF is the length of the string before the CR
	// otherwise the length of the source string
	LOCAL aChars := CHAR[]{nLF} AS CHAR[]
	LOCAL encoding := StringHelpers.WinEncoding AS Encoding
	encoding:GetChars(aBytes, 0, nLF, aChars, 0)
	IF lFoundCRLF
		nBuffLen := nLF+2
	ENDIF
	RETURN STRING{aChars, 0, nLF}

/// Access or allocate the File I/O Buffer used by the X# Runtime for Low Level File Access for a file.
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <param name="nSize">The size of the buffer that you would like to allocate.s</param>
/// <returns>The Byte[] associated with the file handle for an open file.
/// When the file handle is invalid then a NULL object will be returned.</returns>
/// <remarks>This function will either return the existing buffer or allocate a new buffer of the requested size.

/// When there is already a buffer allocated for the file handle, and this buffer is large enough then the existing buffer is returned.
/// When the size requested exceeds the size of the allocated buffer, or when no buffer exists, then a new byte array will be allocated.
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />

FUNCTION FGetBuffer(hFile AS IntPtr, nSize AS INT) AS BYTE[]
    RETURN XSharp.IO.File.GetBuffer(hFile, nSize)

/// Access the FileStream object used by the X# Runtime for Low Level File Access
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <returns>The FileStream object or NULL when the handle is not valid </returns>
/// <remarks><note type="warning">You are not supposed to close the stream object that you retrieve with this function.
/// The Lifetime management of the stream should be left to the X# Runtime

/// If you want to close the stream, please use the FClose() function </note>
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />
FUNCTION FGetStream(pFile AS IntPtr) AS Stream
    RETURN XSharp.IO.File.FindStream(pFile)

/// Returns the size in bytes of a specified file.
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <returns>The size of the file or -1 when the file handle is not valid.</returns>
/// <remarks><note type="warning">You are not supposed to close the stream object that you retrieve with this function.
/// The Lifetime management of the stream should be left to the X# Runtime

/// If you want to close the stream, please use the FClose() function </note>
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />
FUNCTION FSize(pFile AS IntPtr) AS INT64
   VAR oStream := XSharp.IO.File.FindStream(pFile)
    IF oStream != NULL
        RETURN oStream:Length
    ENDIF
    RETURN -1

/// Returns the size in bytes of a specified file.
/// <param name="cFileName">Specifies a file for which FSIZE( ) returns the size in bytes.</param>
/// <returns>The size of the file or -1 when the file is not found.</returns>
/// <seealso cref="M:XSharp.Core.Functions.File(System.String)" />
FUNCTION FSize(cFileName AS STRING) AS INT64
    IF File(cFileName)
        cFileName := FPathName()
        VAR fileInfo := FileInfo{cFileName}
        RETURN fileinfo:Length
    ENDIF
    RETURN 0

//
// Copyright (c) XSharp B.V.  All Rights Reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
//
USING System
USING System.Collections
USING System.Collections.Generic
USING System.IO
USING System.Linq
USING System.Runtime.InteropServices
USING System.Runtime.CompilerServices
USING System.Security
USING System.Runtime
USING System.Runtime.ConstrainedExecution
USING System.Text

#region Defines
	DEFINE F_ERROR :=  IntPtr{-1} // Error value (all functions)

	// FERROR() returns, which are not reflected as DOSERROR()
	DEFINE FERROR_FULL    := 256   // disk full
	DEFINE FERROR_EOF     := 257   // eof was already reached, when a read was tried
	DEFINE FERROR_PARAM   := 258   // invalid parameter already detected before giving to DOS

	// FSEEK(), _llseek() modes
    /// FSeek() Seek Offset: Seek from beginning of file
	DEFINE FS_SET         := 0
    /// FSeek() Seek Offset: Seek from current file position
	DEFINE FS_RELATIVE    := 1
    /// FSeek() Seek Offset: Seek from end of file
	DEFINE FS_END         := 2

	// FOPEN() access modes
    /// FOpen() Open Mode: Open for reading (default)
	DEFINE FO_READ        := 0
    /// FOpen() Open Mode: Open for writing
	DEFINE FO_WRITE       := 1
    /// FOpen() Open Mode: Open for reading or writing
	DEFINE FO_READWRITE   := 2

	// FOPEN() sharing modes (combine with open mode using +)
    /// FOpen() Sharing Mode: Compatibility mode (default)
	DEFINE FO_COMPAT     := 0x00000000
    /// FOpen() Sharing Mode: Exclusive
	DEFINE FO_EXCLUSIVE  := 0x00000010
    /// FOpen() Sharing Mode: Prevent other processes from writing
	DEFINE FO_DENYWRITE  := 0x00000020
    /// FOpen() Sharing Mode: Prevent other processes from reading
	DEFINE FO_DENYREAD   := 0x00000030
    /// FOpen() Sharing Mode: (same as FO_SHARED)
	DEFINE FO_DENYNONE   := 0x00000040
    /// FOpen() Sharing Mode:  Allow other processes to read or write
	DEFINE FO_SHARED     := 0x00000040  //

	DEFINE OF_SHARE_COMPAT		:= 0x00000000
	DEFINE OF_SHARE_EXCLUSIVE	:= 0x00000010
	DEFINE OF_SHARE_DENY_WRITE	:= 0x00000020
	DEFINE OF_SHARE_DENY_READ	:= 0x00000030
	DEFINE OF_SHARE_DENY_NONE	:= 0x00000040
	//DEFINE OF_PARSE				:= 0x00000100

	DEFINE CREATE_NEW := 1
	DEFINE CREATE_ALWAYS := 2
	DEFINE OPEN_EXISTING := 3
	DEFINE OPEN_ALWAYS := 4
	DEFINE FO_DELETE := 0x00000200
	DEFINE FO_VERIFY := 0x00000400
	DEFINE FO_CANCEL := 0x00000800
	DEFINE FO_CREATE := 0x00001000
	DEFINE FO_PROMPT := 0x00002000
	DEFINE FO_EXIST  := 0x00004000
	DEFINE FO_REOPEN := 0x00008000

	// FXOPEN() mode
	DEFINE FXO_WILD      := 0x00010000  // Allow wildcards in file name

	// FCREATE() file attribute modes (always opens with OF_READWRITE)
    /// FCreate() attribute mode: normal read/write file (default for create)
	DEFINE FC_NORMAL     := 0x00000000
    /// FCreate() attribute mode: read-only file
	DEFINE FC_READONLY   := 0x00000001
    /// FCreate() attribute mode: hidden file
	DEFINE FC_HIDDEN     := 0x00000002
    /// FCreate() attribute mode: system file
	DEFINE FC_SYSTEM     := 0x00000004
    /// FCreate() attribute mode: archived
	DEFINE FC_ARCHIVED   := 0x00000020

	//
	// additional file attribute for DIRECTORY(), FFIRST() and FFCOUNT()
	//
	DEFINE FA_VOLUME     := 0x00000008
	DEFINE FA_DIRECTORY  := 0x00000010
	DEFINE FA_NORMAL	 := 0x00000080
	DEFINE FA_TEMPORARY  := 0x00000100
	DEFINE FA_COMPRESSED := 0x00000800
	DEFINE FA_OFFLINE    := 0x00001000

#endregion

BEGIN NAMESPACE XSharp.IO

	INTERNAL CLASS VOFileMode
		PROPERTY lWild			AS LOGIC AUTO
		PROPERTY FileMode		AS FileMode AUTO
		PROPERTY FileAccess		AS FileAccess AUTO
		PROPERTY Attributes	    AS DWORD AUTO
		PROPERTY FileShare		AS FileShare AUTO

		CONSTRUCTOR(dwMode AS DWORD, dwAttribs AS DWORD)
			// Wildcard
			IF (DWORD)_AND(dwMode, FXO_WILD) == FXO_WILD
				lWild := TRUE
				dwMode := dwMode - FXO_WILD
			ELSE
				lWild := FALSE
			ENDIF
			Attributes := dwAttribs

			// Access
			fileAccess	:= FileAccess.read
			IF (DWORD)_AND(dwMode,FO_READWRITE) == FO_READWRITE
				fileAccess	:= FileAccess.readWrite
			ELSEIF (DWORD)_AND(dwMode,FO_WRITE) == FO_WRITE
				fileAccess	:= FileAccess.write
			ENDIF
			// Create
			fileMode := FileMode.Open
			FileShare  := FileShare.readWrite
			IF _AND(dwMode,FO_CREATE) == FO_CREATE
				fileMode	:= FileMode.Create
				fileAccess	:= FileAccess.readWrite
                fileShare   := FileShare.None
                RETURN
			ENDIF

			LOCAL dwTempMode AS DWORD
			dwTempMode := (DWORD)_OR(OF_SHARE_DENY_WRITE, OF_SHARE_DENY_READ, OF_SHARE_DENY_NONE)
			dwTempMode := (DWORD)_AND(dwMode,dwTempMode)

			SWITCH dwTempMode
				CASE FO_DENYNONE
					FileShare  := FileShare.readWrite

				CASE FO_DENYWRITE
					FileShare  := FileShare.read

				CASE FO_DENYREAD
					FileShare  := FileShare.write

				CASE FO_EXCLUSIVE
					FileShare  := FileShare.None

				CASE FO_COMPAT
					FileShare  := FileShare.readWrite

			END SWITCH
			RETURN

	END CLASS

	INTERNAL CLASS FileCacheElement
		PUBLIC PROPERTY Stream	   AS Stream AUTO
		PUBLIC PROPERTY Attributes AS DWORD AUTO
		PUBLIC PROPERTY Bytes	   AS BYTE[] AUTO

		CONSTRUCTOR (oStream AS FileStream, dwAttributes AS DWORD)
			SELF:Attributes := dwAttributes
			SELF:Stream     := oStream
			RETURN
	END CLASS
	/// <exclude />
	INTERNAL STATIC CLASS File
        /// <exclude />
        PUBLIC STATIC errorCode	:= 0 AS DWORD
        PUBLIC STATIC lastException := NULL AS Exception
        /// <exclude />
		PUBLIC STATIC LastFound AS STRING
		PRIVATE STATIC streams AS Dictionary<IntPtr, FileCacheElement >
		PRIVATE STATIC random AS Random

	    /// <exclude />
        STATIC CONSTRUCTOR
			streams := Dictionary<IntPtr, FileCacheElement >{}
			random := Random{}

		STATIC INTERNAL METHOD findStream(pStream AS IntPtr) AS Stream
            LOCAL element := NULL AS FileCacheElement
            IF streams:TryGetValue(pStream, OUT element)
                RETURN element:stream
            ENDIF
			RETURN NULL

		STATIC PRIVATE METHOD hasStream(pStream AS Intptr) AS LOGIC
			RETURN streams:ContainsKey(pStream)

		STATIC PRIVATE METHOD addStream(pStream AS Intptr, oStream AS FileStream, attributes AS DWORD) AS LOGIC
			IF ! streams:ContainsKey(pStream)
				streams:Add(pStream, FileCacheElement {oStream, attributes})
				RETURN TRUE
			ENDIF
			RETURN FALSE

		STATIC INTERNAL METHOD setStream(pStream AS IntPtr, oStream AS Stream) AS LOGIC
            LOCAL element := NULL AS FileCacheElement
            IF streams:TryGetValue(pStream, OUT element)
                element:Stream := oStream
                RETURN TRUE
            ENDIF
			RETURN FALSE

		STATIC PRIVATE METHOD removeStream(pStream AS Intptr) AS LOGIC

			IF streams:ContainsKey(pStream)
				streams:Remove(pStream)
				RETURN TRUE
			ENDIF
			RETURN FALSE

		STATIC PRIVATE METHOD createManagedFileStream(cFIle AS STRING, oMode AS VOFileMode) AS FileStream
			LOCAL oStream := NULL AS FileSTream
			TRY
                		clearErrorState()
				oStream := FileStream{cFile, oMode:FileMode, oMode:FileAccess, oMode:FileShare, 4096}

			CATCH e AS Exception
				System.Diagnostics.Trace.writeLine(e:Message)
				setErrorState(e)
			END TRY
			RETURN oStream

        [MethodImpl(MethodImplOptions.AggressiveInlining)];
        STATIC METHOD ClearErrorState() AS VOID
            lastException := NULL
            errorCode := 0
            RETURN

		STATIC METHOD SetErrorState ( o AS Exception ) AS VOID
            LOCAL e AS Error
            e := Error{o}
            e:StackTrace := o:StackTrace+Environment.NewLine+System.Diagnostics.StackTrace{1,TRUE}:ToString()
            lastException := e
            errorCode := _AND ( (DWORD) System.Runtime.InteropServices.Marshal.GetHRForException ( o ) , 0x0000FFFF )
            e:OsCode := errorCode
            RETURN

		STATIC INTERNAL METHOD CreateFile(cFIle AS STRING, oMode AS VOFileMode) AS IntPtr
			LOCAL hFile := F_ERROR AS IntPtr
			LOCAL oStream AS FileStream
			IF System.IO.File.Exists(cFile) .AND. oMode:FileMode == FileMode.Create
				VAR fi := FileInfo{cFile}
                IF ! fi:IsReadOnly
				    fi:Attributes := FileAttributes.Normal
                ENDIF
			ENDIF

			oStream := createManagedFileStream(cFile, oMode)
			IF oStream != NULL
				hFile := (IntPtr) random:Next(1, Int32.MaxValue)
				DO WHILE streams:ContainsKey(hFile)
					hFile := (IntPtr) random:Next(1, Int32.MaxValue)
				ENDDO
			ELSE
				hFile := F_ERROR
			ENDIF
			//endif
			IF hFile != F_ERROR
				addStream(hFile, oStream, oMode:Attributes)
			ENDIF
			RETURN hFile

		STATIC INTERNAL METHOD GetBuffer(pStream AS IntPtr, nSize AS INT) AS BYTE[]
			IF hasStream(pStream)
				LOCAL element := streams[pStream] AS FileCacheElement
				IF element:Bytes == NULL .OR. element:Bytes:Length < nSize
					element:Bytes := BYTE[]{nSize}
				ENDIF
				RETURN element:Bytes
			ENDIF
			RETURN NULL

		STATIC INTERNAL METHOD close(pStream AS IntPtr) AS LOGIC
			IF hasStream(pStream)
				LOCAL oStream      := streams[pStream]:Stream AS Stream
				LOCAL dwAttributes := streams[pStream]:Attributes AS DWORD
				removeStream(pStream)
                TRY
                    clearErrorState()
				    oStream:Flush()
				    oStream:Close()
				    IF dwAttributes != 0 .AND. oStream IS FileStream VAR oFileStream
					    VAR fi := FileInfo{oFileStream:Name}
					    fi:Attributes := (FileAttributes) dwAttributes
				    ENDIF
				    oStream:Dispose()
				RETURN TRUE
                CATCH e AS Exception
                    setErrorState(e)
                END TRY
			ENDIF
			RETURN FALSE

		INTERNAL STATIC METHOD read(pFile AS Intptr, refC OUT STRING, iCount AS INT, lOem2Ansi AS LOGIC) AS INT
			LOCAL aBuffer := XSharp.IO.File.getBuffer(pFile, iCount) AS BYTE[]
			iCount := (INT) ReadBuff(pFile, aBuffer, iCount, lOem2Ansi)
			refC := Bytes2String(aBuffer, iCount)
			RETURN iCount

		INTERNAL STATIC METHOD readBuff(pFile AS IntPtr,pBuffer AS BYTE[],dwCount AS INT) AS INT64
			LOCAL iResult := 0 AS INT64
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					iResult := oStream:Read(pBuffer,0,(INT) dwCount)
				CATCH e AS Exception
					setErrorState(e)
				END TRY
			ENDIF
			RETURN iResult

		INTERNAL STATIC METHOD readBuff(pFile AS IntPtr,pBuffer AS BYTE[],dwCount AS INT, lAnsi AS LOGIC) AS INT64
            LOCAL iResult := 0 AS INT64
            iResult := readBuff(pFile, pBuffer, dwCount)
            IF FError() == 0 .AND. !lAnsi
				Oem2AnsiA(pBuffer)
			ENDIF
			RETURN iResult

		INTERNAL STATIC METHOD readLine(pFile AS IntPtr,iCount AS INT) AS STRING
			LOCAL cResult := "" AS STRING
			LOCAL nPos	AS INT64
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			IF iCount <= 0
				// According to the VO docs the default value for the buffer length = 256
				iCount := 256
			ENDIF
			IF oStream != NULL_OBJECT
				TRY
					LOCAL pBuffer := XSharp.IO.File.getBuffer(pFile, iCount) AS BYTE[]
                    clearErrorState()
					nPos    := oStream:Position
					iCount := oStream:Read(pBuffer,0,(INT) iCount)
					cResult := Bytes2Line(pBuffer, REF iCount)
					nPos += iCount	// advance CRLF
					oStream:Position := nPos
				CATCH e AS Exception
					setErrorState(e)
				END TRY
			ENDIF
			RETURN cResult

		INTERNAL STATIC METHOD seek(pFile AS IntPtr,lOffset AS INT64,dwOrigin AS DWORD) AS INT64
			LOCAL oStream AS Stream
			LOCAL iResult AS INT64
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				iResult := -1

				TRY
                    clearErrorState()
					SWITCH dwOrigin
						CASE FS_END
							iResult := oStream:Seek(lOffSet, SeekOrigin.End)
						CASE FS_RELATIVE
							iResult := oStream:Seek(lOffSet, SeekOrigin.Current)
						CASE FS_SET
							iResult := oStream:Seek(lOffSet, SeekOrigin.Begin)
						OTHERWISE
							iResult := -1
					END SWITCH
				CATCH e AS Exception
					setErrorState(e)
				END TRY
				RETURN iResult
			ENDIF
			RETURN -1

		INTERNAL STATIC METHOD write( pFile AS IntPtr, c AS STRING, nLength AS INT, lAnsi AS LOGIC ) AS INT
			LOCAL aBytes := String2Bytes(c) AS BYTE[]
			RETURN writeBuff(pFile, aBytes, nLength, lAnsi)

		INTERNAL STATIC METHOD writeBuff(pFile AS IntPtr,pBuffer AS BYTE[],iCount AS INT) AS INT
			LOCAL oStream	AS Stream
			LOCAL iWritten := 0 AS INT
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					oStream:Write(pBuffer, 0, iCount)
					iWritten := iCount
				CATCH e AS Exception
					setErrorState(e)
				END TRY

			ENDIF

			RETURN iWritten

		INTERNAL STATIC METHOD writeBuff(pFile AS IntPtr,pBuffer AS BYTE[],iCount AS INT, lAnsi AS LOGIC) AS INT
			IF !lAnsi
				pBuffer := Ansi2Oem(pBuffer)
            ENDIF
            RETURN writeBuff(pFile, pBuffer, iCount)

		INTERNAL STATIC METHOD writeLine(pFile AS IntPtr,c AS STRING, nLen AS INT) AS INT
			IF c:Length > nLen
				c := c:Substring(0, nLen)
			ENDIF
			RETURN Write(pFile, c + e"\r\n", nLen+2, TRUE)

		INTERNAL STATIC METHOD lock(pFile AS IntPtr,iOffset AS INT64,iLength AS INT64, lLock AS LOGIC) AS LOGIC
			LOCAL oStream := findStream(pFile) AS Stream
			LOCAL lResult := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
                    IF oStream IS FileStream VAR oFileStream
					    IF (lLock)
						    oFileStream:Lock(iOffset, iLength)
					    ELSE
						    oFileStream:UnLock(iOffset, iLength)
                        ENDIF
                        ENDIF
					lResult := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lResult := FALSE
				END TRY
			ENDIF
			RETURN lResult

		INTERNAL STATIC METHOD flush(pFile AS IntPtr, lCommit AS LOGIC) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lOk := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					IF lCommit .AND. oStream IS FileStream VAR oFileStream
						oFileStream:Flush(TRUE)
					ELSE
						oStream:Flush()
					ENDIF
					lOk := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lOk := FALSE
				END TRY
			ENDIF
			RETURN lOk
		INTERNAL STATIC METHOD ChSize(pFile AS IntPtr,nValue AS DWORD) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lOk := FALSE AS LOGIC
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					oStream:SetLength(nValue)
					lOk := TRUE
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
					lOk := FALSE
				END TRY
			ENDIF
			RETURN lOk

		INTERNAL STATIC METHOD Eof(pFile AS IntPtr) AS LOGIC
			LOCAL oStream := XSharp.IO.File.findStream(pFile) AS Stream
			LOCAL lResult := TRUE AS LOGIC
			IF oStream != NULL_OBJECT
				lResult := oStream:Position == oStream:Length
			ENDIF
			RETURN lResult

		INTERNAL STATIC METHOD Tell(pFile AS IntPtr) AS INT64
			LOCAL oStream AS Stream
			oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
				TRY
                    clearErrorState()
					RETURN oStream:Position
				CATCH e AS Exception
					// Catch and save error
					setErrorState(e)
				END TRY
			ENDIF
			RETURN -1

        INTERNAL STATIC  METHOD ConvertToMemoryStream(pFile AS IntPtr) AS VOID
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream != NULL_OBJECT
                  LOCAL oNewStream AS Stream
                 IF oStream IS XsMemoryStream
                        RETURN
                 ENDIF
                 IF oStream:Length >= Int32.MaxValue
                      THROW Error{"Cannot convert stream because input stream is too large: "+oStream:Length:ToString()}
                 ELSE
              	    oNewStream := XsMemoryStream{oStream}
                 ENDIF
                 XSharp.IO.File.setStream(pFile, oNewStream)
                 RETURN
            ENDIF
			RETURN

        INTERNAL STATIC  METHOD ConvertToFileStream(pFile AS IntPtr) AS VOID
			VAR oStream := XSharp.IO.File.findStream(pFile)
			IF oStream IS XsMemoryStream VAR oMemoryStream
                 oMemoryStream:Save()
                 XSharp.IO.File.setStream(pFile, oMemoryStream:FileStream)
                 RETURN
            ENDIF
            THROW Error{"Could not convert stream, source stream is not a Memory Stream"}
			RETURN

	END CLASS

	INTERNAL CLASS XsMemoryStream INHERIT MemoryStream
        INTERNAL FileStream AS Stream
        PRIVATE written AS LOGIC
        CONSTRUCTOR (oFileStream AS Stream)
            SUPER((INT) oFileStream:Length)
            SELF:FileStream := oFileStream
            VAR nPos := SELF:FileStream:Position
            SELF:FileStream:Flush()
            SELF:FileStream:Position := 0
            SELF:FileStream:CopyTo(SELF)
            SELF:FileStream:Position := nPos
            SELF:Position := nPos
            written := FALSE
            RETURN
         INTERNAL METHOD Save() AS VOID
            VAR nPos := SELF:Position
            IF SELF:written
                SELF:Flush()
                SELF:FileStream:SetLength(SELF:Length)
                SELF:FileStream:Position := 0
                SELF:Position := 0
                SELF:CopyTo(SELF:FileStream)
                SELF:FileStream:Position := nPos
                SELF:Position := nPos
            ENDIF
            RETURN

         OVERRIDE METHOD Write(buffer AS BYTE[], offset AS INT, count AS INT) AS VOID
             SUPER:Write(buffer, offset, count)
             written := TRUE

         OVERRIDE METHOD WriteByte(aByte AS BYTE) AS VOID
             SUPER:WriteByte(aByte)
             written := TRUE

         OVERRIDE METHOD SetLength(len AS INT64) AS VOID
             SUPER:SetLength(len)
             written := TRUE

    END CLASS
END NAMESPACE

///
/// Set the error code for a file operation.
///
/// <param name="nSet"></param>
/// <returns>The previous errorcode from the last file operation.</returns>
FUNCTION FError(nErrorCode AS DWORD) AS DWORD
	LOCAL nOldError AS DWORD
	nOldError := XSharp.IO.File.errorCode
	XSharp.IO.File.errorCode := nErrorCode
	RETURN nOldError

///
/// Get the error code for a file operation.
///
/// <returns>The error from the last file operation or the last user-specified setting.  If there was no error, FError() returns 0.</returns>
FUNCTION FError() AS DWORD
	LOCAL nOldError AS DWORD
	nOldError := XSharp.IO.File.errorCode
	RETURN nOldError

///
/// Get the last exception for a file operation.
///
/// <returns>The exception from the last file operation or the last user-specified setting.  If there was no exception, FException() returns null.</returns>
FUNCTION FException() AS Exception
	RETURN XSharp.IO.File.lastException

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fchsize/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FChSize(ptrHandle AS IntPtr,dwOffset AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.Chsize(ptrHandle, dwOffset)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fclose/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FClose(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.Close(ptrHandle)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcommit/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FCommit(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.flush(ptrHandle, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/feof/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FEof(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.eof(ptrHandle)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fflock/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FFLock(ptrHandle AS IntPtr,dwOffset AS DWORD,dwLength AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.lock(ptrHandle, (INT64) dwOffset, (INT64) dwLength, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fflush/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FFlush(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.flush(ptrHandle, TRUE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/ffunlock/*" />
/// <inheritdoc cref="M:XSharp.Core.Functions.FFLock(System.IntPtr,System.UInt32,System.UInt32)" />"
FUNCTION FFUnLock(ptrHandle AS IntPtr,dwOffset AS DWORD,dwLength AS DWORD) AS LOGIC
	RETURN XSharp.IO.File.lock(ptrHandle, (INT64) dwOffset, (INT64) dwLength, FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets/*" />
FUNCTION FGetS(ptrHandle AS IntPtr) AS STRING
	// According to the VO docs the dedault value for the buffer length = 256
	RETURN XSharp.IO.File.readLine(ptrHandle,256)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets/*" />
FUNCTION FGetS(ptrHandle AS IntPtr,nMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) nMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fgets2/*" />
FUNCTION FGetS2(ptrHandle AS IntPtr,dwMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) dwMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs/*" />
FUNCTION FPutS(ptrHandle AS IntPtr,cBuffer AS STRING) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, (DWORD) cBuffer:Length)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs/*" />
FUNCTION FPutS(ptrHandle AS IntPtr,cBuffer AS STRING,nBytes AS DWORD) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, nBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fputs3/*" />
FUNCTION FPutS3(ptrHandle AS IntPtr,cBuffer AS STRING, dwBytes AS DWORD) AS DWORD
	RETURN FWriteLine(ptrHandle, cBuffer, dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread3/*" />
/// <param name="ptrBufferVar">An array of bytes to store the data read from the specified file. The length of this variable must be greater than or equal to the number of bytes in the next parameter.</param>
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FRead3(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread4/*" />
/// <param name="ptrBufferVar">An array of bytes to store the data read from the specified file. The length of this variable must be greater than or equal to the number of bytes in the next parameter.</param>
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FRead4(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD,lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes, lAnsi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadline/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadLine(ptrHandle AS IntPtr,nMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) nMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadline2/*" />
FUNCTION FReadLine2(ptrHandle AS IntPtr,dwMax AS DWORD) AS STRING
	RETURN XSharp.IO.File.readLine(ptrHandle, (INT) dwMax)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadstr/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadStr(ptrHandle AS IntPtr,dwBytes AS DWORD) AS STRING
	LOCAL cResult AS STRING
	XSharp.IO.File.read(ptrHandle, OUT cResult, (INT) dwBytes, XSharp.RuntimeState.Ansi)
	RETURN cResult

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadtext/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadText(ptrHandle AS IntPtr,cBufferVar REF STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.read(ptrHandle, OUT cBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fread/*" />
FUNCTION FRead(ptrHandle AS IntPtr,cBufferVar REF STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.read(ptrHandle, OUT cBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/freadtext3/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FReadText3(ptrHandle AS IntPtr,ptrBufferVar AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.readBuff(ptrHandle, ptrBufferVar, (INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/frewind/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FRewind(ptrHandle AS IntPtr) AS LOGIC
	RETURN XSharp.IO.File.Seek(ptrHandle, 0, FS_SET) == 0

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fseek3/*" />
/// <remarks>
/// The possible values for the origin are:
/// <list type="table">
/// <listheader>
/// <term>Constant</term>           <description>Seeks from</description>
/// </listheader>
///	<item><term>FS_END</term>       <description>End-of-file   </description></item>
/// <item><term>FS_RELATIVE</term>  <description>Current pointer position  </description></item>
/// <item><term>FS_SET</term>       <description>Beginning-of-file </description></item>
/// </list>
/// </remarks>
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FSeek3(ptrHandle AS IntPtr,liOffset AS LONG, dwOrigin AS DWORD) AS LONG
	RETURN (LONG) XSharp.IO.File.Seek(ptrHandle, (INT64) liOffset, dwOrigin)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/ftell/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FTell(ptrHandle AS IntPtr) AS DWORD
	RETURN (DWORD) XSharp.IO.File.Tell(ptrHandle)

/// <inheritdoc cref="M:XSharp.Core.Functions.FTell(System.IntPtr)" />
FUNCTION FTell64(pFile AS IntPtr) AS INT64
	RETURN XSharp.IO.File.Tell(pFile)

/// <inheritdoc cref="M:XSharp.Core.Functions.FWrite(System.IntPtr,System.String,System.UInt32)" />
FUNCTION FWrite( pFile AS IntPtr, c AS STRING ) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write( pFile, c,  c:Length, TRUE )

/// <overloads>
///
/// Write a string to an open file
///
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FWrite( ptrHandle AS IntPtr, cBuffer AS STRING, nBytes AS DWORD ) AS DWORD
	RETURN FWrite( ptrHandle, cBuffer, nBytes, TRUE)

/// <inheritdoc cref="M:XSharp.Core.Functions.FWrite(System.IntPtr,System.String,System.UInt32)" />
/// <param name="lAnsi">If FALSE an OEM to ANSI conversion is made. </param>
FUNCTION FWrite( ptrHandle AS IntPtr, cBuffer AS STRING, nBytes AS DWORD, lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write(ptrHandle, cBuffer, (INT) nBytes, lAnsi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite3/*" />
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FWrite3(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle, ptrBuffer, (INT) dwBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwrite4/*" />
/// <remarks><include file="CoreComments.xml" path="Comments/Oem2AnsiFileIO/*" /></remarks>
FUNCTION FWrite4(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD,lAnsi AS LOGIC) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle,ptrBuffer , (INT) dwBytes ,lAnsi )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline/*" />
FUNCTION FWriteLine(ptrHandle AS IntPtr,cBuffer AS STRING) AS DWORD
	RETURN (DWORD) XSharp.IO.File.write(ptrHandle, cBuffer + e"\r\n",cBuffer:Length+2, TRUE)

/// <overloads>
///
/// Write a string, a carriage-return character, and a linefeed character to an open file.
///
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FWriteLine(ptrHandle AS IntPtr,cBuffer AS STRING,nBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeLine(ptrHandle, cBuffer, (INT) nBytes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwriteline3/*" />
FUNCTION FWriteLine3(ptrHandle AS IntPtr,cBuffer AS STRING,dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeLine(ptrHandle, cBuffer, (INT) dwBytes )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fwritetext3/*" />
FUNCTION FWriteText3(ptrHandle AS IntPtr,ptrBuffer AS BYTE[],dwBytes AS DWORD) AS DWORD
	RETURN (DWORD) XSharp.IO.File.writeBuff(ptrHandle ,ptrBuffer ,(INT) dwBytes, XSharp.RuntimeState.Ansi)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/adjustfname/*" />
FUNCTION AdjustFName(cFileName AS STRING) AS STRING
	LOCAL adjusted := NULL AS STRING
	IF ( !string.IsNullOrEmpty(cFileName) )
		adjusted := System.IO.Path.GetFileNameWithoutExtension(cFileName):TrimEnd()
		IF ( cFileName:IndexOf('.') > 0 )
			adjusted += System.IO.Path.GetExtension(cFileName)
		ENDIF
	ENDIF
	RETURN adjusted

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/adjustfnamea/*" />
FUNCTION AdjustFNameA(cFileName REF STRING) AS STRING
	cFileName := AdjustFName(cFileName)
	RETURN cFileName

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate2/*" />
/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FCreate2(cFileName AS STRING,dwAttributes AS DWORD) AS IntPtr
	LOCAL oFileMode AS VOFileMode
	oFileMode := VOFileMode{ FO_CREATE, dwAttributes }
	RETURN XSharp.IO.File.CreateFile(cFileName, oFileMode)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate/*" />
FUNCTION FCreate(cFileName AS STRING ) AS IntPtr
	RETURN FCreate2(cFileName, _OR(FC_NORMAL, FO_EXCLUSIVE))

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fcreate/*" />
FUNCTION FCreate(cFileName AS STRING ,kAttributes AS DWORD) AS IntPtr
	RETURN FCreate2(cFileName, kAttributes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen/*" />
FUNCTION FOpen(cFileName AS STRING) AS IntPtr
	RETURN FOpen2(cFileName, FC_NORMAL)

/// <overloads>
/// Open a file.
/// <include file="CoreComments.xml" path="Comments/File/*" />
/// </overloads>
/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen/*" />
/// <include file="CoreComments.xml" path="Comments/FileHandle/*" />

/// <include file="CoreComments.xml" path="Comments/File/*" />
FUNCTION FOpen(cFileName AS STRING,kMode AS DWORD) AS IntPtr
	RETURN FOpen2(cFileName, kMode)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fopen2/*" />
FUNCTION FOpen2(cFileName AS STRING,kMode AS DWORD) AS IntPtr
	LOCAL oFileMode AS VOFileMode
	oFileMode := VOFileMode{kMode, 0}
	RETURN XSharp.IO.File.CreateFile(cFileName, oFileMode)

FUNCTION FConvertToMemoryStream(pFile AS IntPtr) AS IntPtr
   TRY
        XSharp.IO.File.ClearErrorState()
        XSharp.IO.File.ConvertToMemoryStream(pFile)
    CATCH e AS Exception
        XSharp.IO.File.setErrorState(e)
        RETURN F_ERROR
   END TRY
   RETURN pFile

FUNCTION FConvertToFileSTream(pFile AS IntPtr) AS IntPtr
   TRY
        XSharp.IO.File.ClearErrorState()
        XSharp.IO.File.ConvertToFileStream(pFile)
    CATCH e AS Exception
        XSharp.IO.File.setErrorState(e)
        RETURN F_ERROR
   END TRY
   RETURN pFile

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfattr/*" />
FUNCTION GetFAttr(uAttributes AS STRING) AS DWORD
	RETURN String2FAttr(uAttributes)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfattr/*" />
FUNCTION GetFAttr(uAttributes AS DWORD) AS DWORD
	RETURN uAttributes

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getfmask/*" />
FUNCTION GetFMask(cString AS STRING) AS STRING
	LOCAL cResult AS STRING
	IF String.IsNullOrEmpty(cString)
		cResult := "*.*"
	ELSE
		VAR cChar := cString[cString:Length-1]
		SWITCH cChar
		CASE ':'
		CASE '\\'
			cResult := cString + "*.*"
		OTHERWISE
			cResult := cString
		END SWITCH
	ENDIF
	RETURN cResult

/// <param name="cAttr"> One or more of the following constants or strings: ADHSRV</param>
/// <returns>
/// </returns>
FUNCTION String2FAttr(cAttr AS STRING) AS DWORD
	LOCAL dwAttributes := FC_NORMAL AS DWORD
	IF !String.IsNullOrEmpty(cAttr)
		FOREACH c AS CHAR IN cAttr
			SWITCH Char.ToUpper(c)
				CASE 'A'
					dwAttributes |= FC_ARCHIVED
				CASE 'C'
					dwAttributes |= FA_COMPRESSED
				CASE 'D'
					dwAttributes |= FA_DIRECTORY
				CASE 'H'
					dwAttributes |= FC_HIDDEN
				CASE 'R'
					dwAttributes |= FC_READONLY
				CASE 'S'
					dwAttributes |= FC_SYSTEM
				CASE 'V'
					dwAttributes |= FA_VOLUME
			END SWITCH
		NEXT
	ENDIF
	RETURN dwAttributes

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/fattr2string/*" />
FUNCTION FAttr2String(dwAttributes AS DWORD) AS STRING
	LOCAL cAttribute := "" AS STRING

	IF (DWORD)_AND(dwAttributes,FA_DIRECTORY) == FA_DIRECTORY
		cAttribute := cAttribute + "D"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FA_VOLUME) == FA_VOLUME
		cAttribute := cAttribute + "V"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_ARCHIVED) == FC_ARCHIVED
		cAttribute := cAttribute + "A"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_HIDDEN) == FC_HIDDEN
		cAttribute := cAttribute + "H"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_READONLY) == FC_READONLY
		cAttribute := cAttribute + "R"
	ENDIF
	IF (DWORD)_AND(dwAttributes,FC_SYSTEM) == FC_SYSTEM
		cAttribute := cAttribute + "S"
	ENDIF

	RETURN cAttribute

/// <exclude/>
FUNCTION Bytes2String(aBytes AS BYTE[], nBuffLen AS INT) AS STRING
	LOCAL aChars := CHAR[]{nBuffLen} AS CHAR[]
	LOCAL encoding := StringHelpers.WinEncoding AS Encoding
	encoding:GetChars(aBytes, 0, nBuffLen, aChars, 0)
	RETURN STRING{aChars, 0, nBuffLen}

/// <exclude/>
FUNCTION String2Bytes(sSource AS STRING) AS BYTE[]
	LOCAL ret AS BYTE[]
	IF sSource != NULL
		LOCAL encoding := StringHelpers.WinEncoding AS Encoding
		ret := encoding:GetBytes( sSource )
	ELSE
		ret := BYTE[]{0}
	ENDIF
	RETURN ret

INTERNAL FUNCTION Bytes2Line(aBytes AS BYTE[], nBuffLen REF INT) AS STRING
	// determine end of line
	// note that VO looks for CR LF. CR alone or LF alone to not count as end of line
	LOCAL nLF AS INT
	LOCAL lFoundCR := FALSE AS LOGIC
	LOCAL lFoundCRLF := FALSE AS LOGIC
	nLF := nBuffLen
	FOR VAR nI := 0 TO nBuffLen-1
		SWITCH aBytes[nI]
			CASE 10
				IF lFoundCR		// immediately LF after CR
					nLF := nI -1
					lFoundCRLF := TRUE
				ENDIF
			CASE 13
				lFoundCR := TRUE
			OTHERWISE
				lFoundCR := FALSE
		END SWITCH
		IF lFoundCRLF
			EXIT
		ENDIF
	NEXT
	// when we have found a CRLF then nLF is the length of the string before the CR
	// otherwise the length of the source string
	LOCAL aChars := CHAR[]{nLF} AS CHAR[]
	LOCAL encoding := StringHelpers.WinEncoding AS Encoding
	encoding:GetChars(aBytes, 0, nLF, aChars, 0)
	IF lFoundCRLF
		nBuffLen := nLF+2
	ENDIF
	RETURN STRING{aChars, 0, nLF}

/// Access or allocate the File I/O Buffer used by the X# Runtime for Low Level File Access for a file.
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <param name="nSize">The size of the buffer that you would like to allocate.s</param>
/// <returns>The Byte[] associated with the file handle for an open file.
/// When the file handle is invalid then a NULL object will be returned.</returns>
/// <remarks>This function will either return the existing buffer or allocate a new buffer of the requested size.

/// When there is already a buffer allocated for the file handle, and this buffer is large enough then the existing buffer is returned.
/// When the size requested exceeds the size of the allocated buffer, or when no buffer exists, then a new byte array will be allocated.
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />

FUNCTION FGetBuffer(hFile AS IntPtr, nSize AS INT) AS BYTE[]
    RETURN XSharp.IO.File.GetBuffer(hFile, nSize)

/// Access the FileStream object used by the X# Runtime for Low Level File Access
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <returns>The FileStream object or NULL when the handle is not valid </returns>
/// <remarks><note type="warning">You are not supposed to close the stream object that you retrieve with this function.
/// The Lifetime management of the stream should be left to the X# Runtime

/// If you want to close the stream, please use the FClose() function </note>
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />
FUNCTION FGetStream(pFile AS IntPtr) AS Stream
    RETURN XSharp.IO.File.FindStream(pFile)

/// Returns the size in bytes of a specified file.
/// <param name="pFile"><include file="CoreComments.xml" path="Comments/FileHandle/*" /></param>
/// <returns>The size of the file or -1 when the file handle is not valid.</returns>
/// <remarks><note type="warning">You are not supposed to close the stream object that you retrieve with this function.
/// The Lifetime management of the stream should be left to the X# Runtime

/// If you want to close the stream, please use the FClose() function </note>
/// </remarks>
/// <seealso cref="M:XSharp.Core.Functions.FClose(System.IntPtr)" />
FUNCTION FSize(pFile AS IntPtr) AS INT64
   VAR oStream := XSharp.IO.File.FindStream(pFile)
    IF oStream != NULL
        RETURN oStream:Length
    ENDIF
    RETURN -1

/// Returns the size in bytes of a specified file.
/// <param name="cFileName">Specifies a file for which FSIZE( ) returns the size in bytes.</param>
/// <returns>The size of the file or -1 when the file is not found.</returns>
/// <seealso cref="M:XSharp.Core.Functions.File(System.String)" />
FUNCTION FSize(cFileName AS STRING) AS INT64
    IF File(cFileName)
        cFileName := FPathName()
        VAR fileInfo := FileInfo{cFileName}
        RETURN fileinfo:Length
    ENDIF
    RETURN 0


[VOSDK] SqlStatement is no longer a partial class

PARTIAL CLASS SQLStatement

	HIDDEN  hStmt   			AS PTR
	HIDDEN  cStatement      AS STRING
	HIDDEN  oConn           AS SQLConnection
	HIDDEN  oErrInfo        AS SQLErrorInfo
	HIDDEN  lPrepFlag       AS LOGIC
	HIDDEN  aParams[0]      AS ARRAY
	PROTECT nRowSet         AS INT
	HIDDEN  nCursorType     AS INT
	HIDDEN  nSimulateCursor AS INT
	PROTECT nKeySet         AS INT
//	HIDDEN  nRecCount       AS LONGINT // Unused
	PROTECT aUserOption     AS ARRAY
	METHOD __AllocStmt() AS LOGIC STRICT
	LOCAL hStmtNew AS PTR
	LOCAL nRetCode AS INT
	LOCAL lRet     AS LOGIC

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:__AllocStmt()" )
	#ENDIF

	IF hStmt = SQL_NULL_HSTMT
		//RvdH 050413 This should be replaced with SqlAllocHandle
		nRetCode := SQLAllocStmt( oConn:ConnHandle, @hStmtNew )
		IF nRetCode = SQL_SUCCESS
			oErrInfo:ErrorFlag := FALSE
			SELF:hStmt := hStmtNew
			#IFDEF __DEBUG__
				__SQLOutputDebug( "** hDbc="+AsString( oConn:ConnHandle )+", hStmt="+AsString( hStmt ) )
			#ENDIF
			oConn:__RegisterStmt( SELF )
			lRet := TRUE
		ELSE
			SELF:MakeErrorInfo(SELF, #AllocStmt, nRetCode)
		ENDIF

	ELSE
		// Statement already allocated
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__STMT_ALLOC ), #AllocStmt )
	ENDIF

	RETURN lRet

METHOD __FreeParameters()
	LOCAL nPar AS DWORD
	LOCAL oPar AS SqlParameter
	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Axit(), hStmt = "+AsString( hStmt ) )
	#ENDIF
	IF ! SELF:__FreeStmt(SQL_RESET_PARAMS)
		RETURN FALSE
	ENDIF
	// Destroy 'Our' Parameter objects
	FOR nPar := 1 TO ALen(SELF:aParams)
		IF (IsInstanceOfUsual(aParams[nPar], #SQLParameter))
			oPar := aParams[nPar]
			IF oPar:InternalParam
				oPar:Destroy()
			ENDIF
		ENDIF
	NEXT
	ASize(SELF:aParams,0)
	RETURN TRUE

METHOD __FreeStmt( nOption AS WORD) AS LOGIC STRICT
	LOCAL nRetCode AS INT
	LOCAL lRet     AS LOGIC

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:__FreeStmt( "+AsString( nOption )+" ) hStmt="+AsString( hStmt ) )
	#ENDIF

	IF hStmt != SQL_NULL_HSTMT

		IF nOption = SQL_DROP
			SQLFreeStmt( hStmt, SQL_UNBIND )
			nRetCode := SQLFreeStmt( hStmt, SQL_CLOSE )
			//RvdH 050413 This should be replaced with a Call to SQLFreeHandle()
			nRetCode := SQLFreeStmt( hStmt, SQL_DROP )
		ELSE
			nRetCode := SQLFreeStmt( hStmt, nOption )
		ENDIF

		IF nRetCode = SQL_SUCCESS

			oErrInfo:ErrorFlag := FALSE
			IF nOption = SQL_DROP
				SELF:oConn:__UnRegisterStmt( SELF )
				SELF:hStmt := SQL_NULL_HSTMT
				lPrepFlag := FALSE

			ELSEIF nOption = SQL_CLOSE
				lPrepFlag := FALSE
			ENDIF

			lRet := TRUE
		ELSE
			SELF:MakeErrorInfo(SELF, #FreeStmt, nRetCode)
		ENDIF
	ENDIF
	RETURN lRet

METHOD __GenerateSQLError( cErrorString AS STRING, symMethod AS SYMBOL) AS SQLErrorInfo STRICT

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:__GenerateSQLError( "+         ;
			AsString( cErrorString )+","+AsString( symMethod )+" )" )
	#ENDIF

	oErrInfo:ErrorMessage := __CavoStr( __CAVOSTR_SQLCLASS__ODBC_VO ) +      ;
		Symbol2String( ClassName( SELF ) ) +   ;
		":" + Symbol2String( symMethod ) +    ;
		" " + cErrorString
	oErrInfo:NativeError:= 0
	oErrInfo:SQLState   := __CavoStr( __CAVOSTR_SQLCLASS__GENERAL_ERR )
	oErrInfo:ErrorFlag  := TRUE
	oErrInfo:SubSystem  := __CavoStr( __CAVOSTR_SQLCLASS_SUBSYS )
	oErrInfo:MethodSelf := SELF
	oErrInfo:FuncSym    := symMethod
	oErrInfo:CallFuncSym:= symMethod
	oErrInfo:Severity   := ES_ERROR
	oErrInfo:ReturnCode := SQL_SUCCESS

	RETURN oErrInfo

ACCESS __Params AS ARRAY STRICT

	RETURN SELF:aParams

ASSIGN __Params( aNewParams AS ARRAY)  STRICT
	RETURN

METHOD __Reset( ) AS LOGIC STRICT
	LOCAL lRet AS LOGIC

	IF hStmt != SQL_NULL_HSTMT
		IF SELF:__FreeStmt( SQL_CLOSE )
			lRet := SELF:Execute()
		ENDIF
	ENDIF
	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:__ResetStmt() returns " + AsString( lRet ) )
	#ENDIF

	RETURN lRet

METHOD __SetDefaultStatementOptions() AS LOGIC STRICT
	LOCAL n     AS INT

	IF SELF:nCursorType > 0
      n := SELF:nCursorType
      SELF:SetStatementOption( SQL_CURSOR_TYPE, n , TRUE)
	ENDIF

	// If keyset driven, set keyset size
	IF ( SELF:nCursorType == SQL_CURSOR_KEYSET_DRIVEN )
		IF SELF:nKeySet > 0
         n := SELF:nKeySet
         SELF:SetStatementOption( SQL_KEYSET_SIZE, n , TRUE)
		ENDIF
	ENDIF

	IF SELF:nRowSet > 0
      n := SELF:nRowSet
      SELF:SetStatementOption( SQL_ROWSET_SIZE, n , TRUE)
	ENDIF

	// Set concurrency level
	n := SELF:ScrollConcurrency

	IF n > 0
      SELF:SetStatementOption( SQL_CONCURRENCY, n , TRUE)
	ENDIF

	RETURN TRUE

METHOD __SetParameters( aNewParams AS ARRAY) AS LOGIC STRICT
	LOCAL nIndex        AS DWORD
	LOCAL nNumParams    AS DWORD
	LOCAL nRetCode      AS INT
	LOCAL uParam		  AS USUAL
	LOCAL oParam		  AS SqlParameter

	IF ( ! SELF:__FreeStmt( SQL_RESET_PARAMS ) )
		RETURN FALSE
	ENDIF

	nNumParams := ALen( aNewParams )
	ASize(SELF:aParams, nNumParams)
	FOR nIndex := 1 UPTO nNumParams
		uParam 	:= aNewParams[nIndex]
		IF IsInstanceOfUsual(uParam, #SqlParameter)
			oParam := uParam
			// Do not set the 'internalparam' flag
		ELSE
			oParam := SqlParameter{uParam}
			oParam:InternalParam := TRUE
		ENDIF
		SELF:aParams[nIndex] := oParam
      nRetCode := oParam:bind(SELF, nIndex)

		IF ( nRetCode != SQL_SUCCESS )
			SELF:MakeErrorInfo(SELF, #SetParameters, nRetCode)
			RETURN FALSE
		ENDIF

	NEXT
	SELF:__ErrInfo:ErrorFlag := FALSE
	RETURN TRUE

METHOD __SetScrollOptions( nConcType AS DWORD, nKeySet AS DWORD, lAsync AS LOGIC) AS LOGIC STRICT
	LOCAL lRet AS LOGIC

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:SetScrollOptions()" )
	#ENDIF

	lRet := TRUE

	lRet := SELF:SetStatementOption( SQL_CURSOR_TYPE, nKeySet, .F. )
	IF lRet
		lRet := SELF:SetStatementOption( SQL_CONCURRENCY, nConcType, .F. )
	ENDIF
	IF lRet
		lRet := SELF:SetStatementOption( SQL_ROWSET_SIZE, SELF:nRowSet, .F. )
	ENDIF
	IF lRet
		lRet := SELF:SetStatementOption( SQL_ASYNC_ENABLE, IIF( lAsync, 1, 0 ), .F. )
	ENDIF

	IF lRet
		oErrInfo:ErrorFlag := FALSE
	ELSE
		SELF:MakeErrorInfo(SELF,   #__SetScrollOptions,0)
	ENDIF

	RETURN lRet

DESTRUCTOR()
	SELF:Destroy()
	RETURN

METHOD Commit()
	//
	//  UH: Obsolete method, don't support after 2.0 !!!!
	//
	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Commit()" )
	#ENDIF

	RETURN SELF:__Connection:Commit()

ACCESS __Connection as SqlConnection
    RETURN SELF:oConn
ACCESS Connection

	RETURN SELF:oConn

ACCESS CursorType

	RETURN SELF:GetStatementOption( SQL_CURSOR_TYPE )

ASSIGN CursorType( nVal )

	IF IsNumeric( nVal ) .AND. ;
			( nVal == SQL_CURSOR_FORWARD_ONLY .OR. ;
			nVal == SQL_CURSOR_KEYSET_DRIVEN .OR. ;
			nVal == SQL_CURSOR_DYNAMIC .OR. ;
			nVal == SQL_CURSOR_STATIC )
		RETURN SELF:nCursorType := nVal
	ENDIF
	RETURN SELF:nCursorType

METHOD Destroy()
	IF hStmt != SQL_NULL_HSTMT .AND. oConn != NULL_OBJECT
		IF oConn:Connected
		   SELF:__FreeParameters()
			SELF:__FreeStmt( SQL_DROP )
			//IF ! InCollect()
				SELF:hStmt := SQL_NULL_HSTMT
				UnregisterAxit(SELF)
			//ENDIF
		ENDIF
	ENDIF
	RETURN NIL

ACCESS __ErrInfo as SqlErrorInfo
    return oErrInfo

ACCESS ErrInfo

	RETURN oErrInfo

ASSIGN ErrInfo( uVal )

	SELF:oErrInfo := uVal

	RETURN SELF:oErrInfo

METHOD Execute( uParm )
	LOCAL nCount     AS DWORD
	LOCAL aArg       AS ARRAY
	LOCAL nRetCode   AS INT
	LOCAL l          AS LONGINT
	LOCAL lRet       AS LOGIC
	LOCAL lNewParams AS LOGIC

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Execute()" )
	#ENDIF

	IF hStmt = SQL_NULL_HSTMT
		SELF:__AllocStmt()
	ENDIF

	nCount := ALen( SELF:aParams )

	IF PCount() != 0
		IF PCount() = 1 .AND. UsualType( uParm ) = ARRAY
			aArg   := uParm
			nCount := ALen( uParm )
		ELSE
			aArg := {}

			FOR nCount := 1 UPTO PCount()
				AAdd( aArg, _GETMPARAM( nCount ) )
			NEXT
		ENDIF
		lNewParams := TRUE
	ENDIF

	IF nCount != 0
		IF ! lNewParams
			aArg := SELF:aParams
		ENDIF
		IF !SELF:__SetParameters( aArg )
			SELF:__FreeParameters()
			RETURN FALSE
		ENDIF
	ENDIF
	IF ALen(SELF:aParams) > 0 .AND. ! lPrepFlag
		SELF:Prepare()
	ENDIF
	IF !lPrepFlag
		IF !SELF:__SetDefaultStatementOptions()
			IF !( oErrInfo:SQLState = __CavoStr( __CAVOSTR_SQLCLASS__FUNCSEQ_ERR ) )
				SELF:oConn:ScrollCsr := FALSE
			ENDIF
		ENDIF

		l := LONGINT( _CAST,SLen( cStatement ) )

		#IFDEF __DEBUG__
			IF DWORD( _CAST, l ) != PszLen( String2Psz( cStatement ) )
				__SQLOutputDebug( "**      STATEMENT CONTAINS embedded ZERORs" )
				//  "Embedded zeros found"
				MemoWrit( "c:\sqlstat.log", cStatement )
			ENDIF
			__SQLOutputDebug( "**              MemTotal() -> " + NTrim( MemTotal() ) )
		#ENDIF
		//_DebOut32(String2Psz("SQLExecDirect start "+NTrim(Seconds())))
		IF l > 0
			nRetCode := SQLExecDirect( hStmt, String2Psz( cStatement ), l )
		ENDIF
		//_DebOut32(String2Psz("SQLExecDirect ok "+NTrim(Seconds())))
		#IFDEF __DEBUG__
			__SQLOutputDebug( "**              SQLExecDirect()" )
			__SQLOutputDebug( "**              MemTotal() -> " + NTrim( MemTotal() ) )
		#ENDIF

	ELSE
		//_DebOut32(String2Psz("SQLExecute start "+NTrim(Seconds())))
		nRetCode := SQLExecute( hStmt )
		//_DebOut32(String2Psz("SQLExecute ok "+NTrim(Seconds())))
	ENDIF

	// SQL_SUCCESS_WITH_INFO is no error
	// We should still check the results here though,
	// I have refrained from doing this here to keep the
	// changes small.
	IF nRetCode = SQL_SUCCESS .OR. nRetCode = SQL_SUCCESS_WITH_INFO
		lPrepFlag := TRUE
		lRet      := TRUE

		// Try to determine the number of rows
		// SQLRowCount is only guaranteed to work after UPDATE,
		// INSERT or DELETE operations. Some drivers however
		// may also be able to return the number of rows
		// of a SELECT statement even before fetching any rows.
		// So this is still worth a try.

	ELSE
		SELF:MakeErrorInfo(SELF, #Execute, nRetCode)
	ENDIF

	#IFDEF __DEBUG__
		__SQLOutputDebug( "**** returns : " + AsString( lRet ) )
	#ENDIF
	//SELF:__FreeParameters()
	RETURN lRet

METHOD FreeStmt( fOption )
	//RvdH 070530 Added default value, as suggested by Stavros Spanos
	IF PCount() == 0
		fOption := SQL_CLOSE
	ENDIF

	RETURN SELF:__FreeStmt( fOption )

METHOD GetStatementOption( fOption )
	LOCAL nValue   AS INT
	LOCAL pData    AS PTR
	LOCAL nRetCode AS INT
	LOCAL xRet     AS USUAL
	//
	//  Get current setting for a statement option; returns NIL if err
	//

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:GetStatementOption()" )
	#ENDIF

	IF hStmt != SQL_NULL_HSTMT
		pData := @nValue
		//RvdH 050413 This should be replaced with a Call to SQLGetStmtAttr()

		nRetCode := SQLGetStmtOption( hStmt, fOption, pData )

		IF nRetCode = SQL_SUCCESS
			oErrInfo:ErrorFlag := FALSE
			xRet := nValue
		ELSE
			SELF:MakeErrorInfo(SELF,#GetStatementOption, nRetCode)
		ENDIF
	ELSE
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NO_STMT ), #GetStatementOption )
	ENDIF
	RETURN xRet

ACCESS HyperLabel
	LOCAL oHL       AS HyperLabel

	oHL := HyperLabel{  #Statement,     ;
							cStatement,     ;
							Symbol2String( ClassName( SELF ) )+ ": " + cStatement,  ;
							Symbol2String( ClassName( SELF ) )+ "_" + cStatement }
	RETURN oHL

CONSTRUCTOR( cSQLStatement, oSQLConnection )

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Init( "+AsString( cSQLStatement )+" )" )
	#ENDIF
	lPrepFlag := FALSE
	// Filled in with the number of rows affected by the next SQLExecDirect call.
	oErrInfo  := SQLErrorInfo{}
	IF IsString( cSQLStatement ) .AND. SLen( cSQLStatement ) > 0
      SELF:cStatement := cSQLStatement
   ENDIF
	IF oSQLConnection = NIL
		SELF:oConn := SQLSetConnection()
	ELSE
		SELF:oConn := oSQLConnection
	ENDIF

	SELF:ScrollConcurrency  := SqlSetStmtConcurrency()
	SELF:CursorType         := SqlSetStmtCursorType()
	SELF:SimulateCursor     := SqlSetStmtSimulateCursor()

	IF oConn:Connected
		SELF:__AllocStmt()
	ELSE
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NOT_CONN ), #Init )
	ENDIF

	nRowSet := 1
	nKeySet := 128
	SELF:aUserOption := {}
	SELF:aParams := {}
	RETURN

ACCESS KeySet
	RETURN SELF:nKeySet

ASSIGN KeySet( n )
	IF IsNumeric( n )
		SELF:nKeySet := Integer( n )
	ENDIF
	RETURN SELF:nKeySet

METHOD MakeErrorInfo(oObject, symMethod, nRetCode)
	SELF:ErrInfo := SQLErrorInfo{  oObject, symMethod,            ;
											SELF:oConn:EnvHandle,         ;
											SELF:oConn:ConnHandle,        ;
											SELF:hStmt }
	SELF:__ErrInfo:ReturnCode := nRetCode
	RETURN SELF:ErrInfo

ACCESS NativeSQL
	LOCAL nBytes        AS INT // dcaton 070206 was DWORD
	LOCAL nSize         AS DWORD
	LOCAL cValueNew     AS STRING
	LOCAL nRetCode      AS INT
	LOCAL pszStmt       AS PSZ

	IF cStatement == NULL_STRING .OR. hStmt = SQL_NULL_HSTMT
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NO_STMT ), #NativeSQL )
	ELSE

		nSize 	:= SLen( cStatement ) * 2 + 1
		pszStmt := MemAlloc( nSize )
		IF pszStmt == NULL_PSZ
			SQLThrowOutOfMemoryError()
			nRetCode := SQL_ERROR
		ELSE
			nRetCode := SQLNativeSql(   oConn:ConnHandle,       ;
											String2Psz( cStatement ), ;
											_SLen( cStatement ) ,       ;
											pszStmt,                ;
											SHORTINT( _CAST,nSize - 1 ),              ;
											@nBytes )

		ENDIF
		IF nRetCode = SQL_SUCCESS
			oErrInfo:ErrorFlag := FALSE
			cValueNew := Psz2String( pszStmt )
		ELSE
			SELF:MakeErrorInfo(SELF, #NativeSql, nRetCode)
		ENDIF
		MemFree( pszStmt )
	ENDIF
	RETURN cValueNew

ACCESS NumParameters
	LOCAL nCount    AS SHORTINT // dcaton 070206 was INT
	LOCAL nRetCode  AS INT

	IF ( hStmt = SQL_NULL_HSTMT )
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NO_STMT ), #NumParameters )
		nCount := -1
	ELSE
		nRetCode := SQLNumParams( hStmt, @nCount )

		IF nRetCode = SQL_SUCCESS
			oErrInfo:ErrorFlag := FALSE

		ELSE
			SELF:MakeErrorInfo(SELF, #NumParameters, nRetCode)
			nCount := -1
		ENDIF
	ENDIF

	RETURN nCount

ACCESS NumSuccessfulRows
	LOCAL nCount    AS INT
	LOCAL nRetCode  AS INT

	IF hStmt = SQL_NULL_HSTMT
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NO_STMT ), #NumSuccessfulRows )
		nCount := -1
	ELSE

		nRetCode := SQLRowCount( hStmt, @nCount )

		#IFDEF __DEBUG__
			__SQLOutputDebug( "** SQLRowCount() returns " + NTrim( nRetCode ) )
			__SQLOutputDebug( "** Number of rows " + NTrim( nCount ) )
		#ENDIF

		IF nRetCode = SQL_SUCCESS
			oErrInfo:ErrorFlag := FALSE
		ELSE
			SELF:MakeErrorInfo(SELF,#NumSuccessfulRows, nRetCode)
			nCount := -1
		ENDIF

	ENDIF

	RETURN nCount

METHOD Prepare()

	LOCAL nRetCode AS INT
	LOCAL lRet     AS LOGIC

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Prepare()" )
	#ENDIF

	IF hStmt = SQL_NULL_HSTMT
		IF !SELF:__AllocStmt()
			RETURN lRet
		ENDIF
	ENDIF

	IF SELF:oConn:ScrollCsr
		// Set all required options
		IF !SELF:__SetDefaultStatementOptions()
			IF !( oErrInfo:SQLState = __CavoStr( __CAVOSTR_SQLCLASS__FUNCSEQ_ERR ) )
				SELF:oConn:ScrollCsr := FALSE
			ENDIF
		ENDIF

	ENDIF

	nRetCode := SQLPrepare( hStmt, String2Psz( cStatement ), LONGINT( _CAST, SLen( cStatement ) ) )
	///RvdH
	// SQL_SUCCESS_WITH_INFO is no error
	// We should still check the results here though,
	// I have refrained from doing this here to keep the
	// changes small.
	IF nRetCode = SQL_SUCCESS .OR. nRetCode = SQL_SUCCESS_WITH_INFO
		oErrInfo:ErrorFlag := FALSE
		lPrepFlag   := TRUE
		lRet        := TRUE
	ELSE
		SELF:MakeErrorInfo(SELF, #Prepare,  nRetCode)
	ENDIF

	RETURN lRet

ACCESS PrepFlag

	RETURN SELF:lPrepFlag

ASSIGN PrepFlag( lNew )

	IF IsLogic( lNew )
		SELF:lPrepFlag := lNew
	ENDIF

	RETURN SELF:lPrepFlag

ACCESS RecCount

	RETURN SELF:NumSuccessfulRows

METHOD RollBack()
	//
	//  UH: Obsolete method, don't support after 2.0 !!!!
	//
	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:Rollback()" )
	#ENDIF

	RETURN SELF:__Connection:RollBack()

ACCESS RowSet

	RETURN SELF:nRowSet

ASSIGN RowSet( nVal )

	IF IsNumeric( nVal ) .AND. nVal > 0

		//  UH: To implement buffering for scrollable cursors
		//  SELF:nRowSet := nVal
		SELF:nRowSet := 1

	ENDIF
	RETURN SELF:nRowSet

ACCESS ScrollConcurrency
	RETURN SELF:oConn:ScrollConcurrency

ASSIGN ScrollConcurrency( nVal )

	RETURN SELF:oConn:ScrollConcurrency := nVal

METHOD SetStatementOption( fOption, uValue, lUser )

	//
	//  Sets current setting for a statement option; returns TRUE if successful
	//
	LOCAL nRetCode AS INT
	LOCAL nExist   AS INT
	LOCAL lRet     AS LOGIC
   LOCAL nOption  AS DWORD

	#IFDEF __DEBUG__
		__SQLOutputDebug(   "** SQLStatement:SetStatementOption( " + ;
			AsString( fOption ) + "," + AsString( uValue ) + " )" )
	#ENDIF

	DEFAULT( @lUser, TRUE )

	nExist := AScan( SELF:aUserOption, fOption )

	IF lUser
		IF nExist = 0
			AAdd( aUserOption, fOption )
		ENDIF
	ELSE
		IF nExist > 0
			//
			//  Don't overwrite option set by user
			//
			RETURN FALSE
		ENDIF
	ENDIF

	IF hStmt = SQL_NULL_HSTMT
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__NO_STMT ), #SetStatementOption )
	ELSE
		//RvdH 050413 This should be replaced with a Call to SQLSetStmtAttr()
      nOption  := uValue
      nRetCode := SQLSetStmtOption( hStmt, fOption, nOption )

		#IFDEF __DEBUG__
			__SQLOutputDebug( "** nRetCode : " + NTrim( nRetCode ) )
		#ENDIF

		IF ( nRetCode = SQL_SUCCESS ) .OR. ( nRetCode = SQL_SUCCESS_WITH_INFO )
			oErrInfo:ErrorFlag := FALSE
			lRet := TRUE
		ELSE
			SELF:MakeErrorInfo(SELF, #SetStatementOption, nRetCode)
		ENDIF
	ENDIF
	RETURN lRet

ACCESS SimulateCursor
	RETURN SELF:GetStatementOption( SQL_SIMULATE_CURSOR )

ASSIGN SimulateCursor( nVal )

	IF IsNumeric( nVal ) .AND. ;
			( nVal == SQL_SC_NON_UNIQUE .OR. ;
			nVal == SQL_SC_TRY_UNIQUE .OR. ;
			nVal == SQL_SC_UNIQUE )

		RETURN SELF:nSimulateCursor := nVal
	ENDIF
	RETURN SELF:nSimulateCursor

ACCESS SQLString

	RETURN cStatement

ASSIGN SQLString( uVal )
	LOCAL cRet  AS STRING

	#IFDEF __DEBUG__
		__SQLOutputDebug( "** SQLStatement:SQLString uVal="+AsString( uVal ) )
	#ENDIF
	EnforceType(@uVal, STRING)
	cRet := SqlDeleteWhiteSpace(uVal )
	// RvdH 061002 No need to reassign if there are no differences
	//	This prevents unneccesarry dropping of prepared statements
	// RvdH 070118 Optimization removed because of problems with parameterized queries
	//IF ! (SELF:cStatement == cRet)
	// statement NOT yet prepared?
	IF ( !lPrepFlag )
		SELF:cStatement := cRet
	ELSE
		// Attempt to drop, then re-prepare the new stmt...
		SELF:FreeStmt( SQL_DROP )
		IF SELF:__AllocStmt()
			SELF:cStatement := cRet
		ENDIF
	ENDIF
	//ENDIF
	RETURN

ACCESS StatementHandle

	RETURN hStmt

ASSIGN StatementHandle( uVal )

	IF hStmt = SQL_NULL_HSTMT
		SELF:hStmt := uVal
		RETURN SELF:hStmt
	ELSE
		SELF:__GenerateSQLError( __CavoStr( __CAVOSTR_SQLCLASS__STMT_NOT_ALLOC ), #StatementHandle )
	ENDIF
	RETURN

ACCESS Status
	LOCAL   oRet    AS OBJECT

	IF SELF:oErrInfo:ErrorFlag
		oRet := HyperLabel{ oErrInfo:FuncSym,                           ;
			oErrInfo:SQLState,                          ;
			Symbol2String( ClassName( oErrInfo:MethodSelf ) ) + ": " + ;
			oErrInfo:ErrorMessage }
	ENDIF

	RETURN oRet

//RvdH 2010-12-03: Some extra properties
ACCESS Params
	RETURN SELF:aParams

END CLASS