Removed overload for SetDateCountry() because it gives compiler warnings

//
// 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.
//
// Most of these settings will Get and Set properties of the Runtime.State class
#include "GetSet.xh"

USING XSharp

///
/// Returns a string representing the morning extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION GetAMExt() AS STRING
	GETSTATE STRING Set.AmExt

///
/// Returns a string representing the morning extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetAMExt() AS STRING
	GETSTATE STRING Set.AmExt

///
/// Set the morning extension for time strings in 12-hour format.
///
/// <param name="cExt"></param>
/// <returns>
/// </returns>
FUNCTION SetAMExt(cExt AS STRING) AS STRING
	SETSTATE STRING Set.AmExt cExt

///
/// Returns the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <returns>
/// </returns>
FUNCTION GetAmPm() AS LOGIC
	GETSTATE LOGIC Set.AmPm

///
/// Returns the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetAmPm() AS LOGIC
	GETSTATE LOGIC Set.AmPm

///
/// Change the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetAmPm(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.AmPm lSet

///
/// Return and the setting that determines whether database files are created using ANSI or OEM format and whether certain text file operations convert between the two character sets.
///
/// <returns>
/// </returns>
FUNCTION SetAnsi() AS LOGIC
	RETURN RuntimeState.Ansi

///
/// Change the setting that determines whether database files are created using ANSI or OEM format and whether certain text file operations convert between the two character sets.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetAnsi(lSet AS LOGIC) AS LOGIC
	LOCAL lOld := RuntimeState.Ansi AS LOGIC
	RuntimeState.Ansi := lSet
	RETURN lOld

///
/// Return the setting that determines whether a beep is sounded by the error system when an error occurs.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetBeep() AS LOGIC
	GETSTATE LOGIC Set.BELL

///
/// Change the setting that determines whether a beep is sounded by the error system when an error occurs.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetBeep(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.BELL lSet

///
/// Return the setting that determines whether to include or omit century digits in the date format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetCentury() AS LOGIC
	GETSTATE LOGIC Set.Century

///
/// Change the setting that determines whether to include or omit century digits in the date format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetCentury(lSet AS LOGIC) AS LOGIC
	LOCAL lOld AS LOGIC
    lOld := XSharp.RuntimeState.GetValue<LOGIC>(Set.CENTURY)
    IF lOld != lSet
      VAR cFormat := XSharp.RuntimeState.DateFormat

      IF lSet
        IF ! cFormat:Contains("YYYY")
            cFormat := cFormat:Replace( "YY", "YYYY" )
        ENDIF
        ELSE
            IF cFormat:Contains("YYYY")
                cFormat := cFormat:Replace( "YYYY", "YY" )
            ENDIF
      ENDIF

      SetDateFormat( cFormat )

    ENDIF
    RETURN lOld

///
/// Return the setting that determines the type of central processor you have.
///
/// <param name="nCpu"></param>
/// <returns>
/// </returns>
FUNCTION SetCpu() AS DWORD
	GETSTATE DWORD Set.CPU

///
/// Change the setting that determines the type of central processor you have.
///
/// <param name="nCpu"></param>
/// <returns>
/// </returns>
FUNCTION SetCpu(nCpu AS DWORD) AS DWORD
	SETSTATE DWORD Set.CPU nCPU

///
/// Return the setting that determines the X# date format by selecting from a list of constants with corresponding date formats.
///
/// <param name="dwCountry"></param>
/// <returns>
/// </returns>
FUNCTION SetDateCountry() AS DWORD
	GETSTATE DWORD Set.DATECOUNTRY

///
/// Return and optionally change the setting that determines the X# date format by selecting from a list of constants with corresponding date formats.
///
/// <param name="dwCountry"></param>
/// <returns>
/// </returns>
FUNCTION SetDateCountry(dwCountry AS DWORD) AS DWORD
//	setstate DWORD Set.DATECOUNTRY dwCountry
	LOCAL dwOld := RuntimeState.DateCountry AS DWORD
	RuntimeState.DateCountry := dwCountry
	RETURN dwOld

///
/// Return the current Date format.
///
/// <returns>
/// </returns>
FUNCTION GetDateFormat() AS STRING
	GETSTATE STRING Set.DateFormat

///
/// Change the setting that determines the X# date format.
///
/// <param name="cDateFormat"></param>
/// <returns>
/// </returns>
FUNCTION SetDateFormat(cDateFormat AS STRING) AS STRING
	LOCAL cOld AS STRING
	// Changing Dateformat also changes DateCountry and Century
	cOld := RuntimeState.DateFormat
	RuntimeState.DateFormat := cDateFormat
	RETURN cOld

///
/// Return the setting that determines the number of decimal places used to display numbers.
///
/// <returns>
/// </returns>
FUNCTION SetDecimal() AS DWORD
	GETSTATE DWORD Set.Decimals

///
/// Return and change the setting that determines the number of decimal places used to display numbers.
///
/// <param name="nDec"></param>
/// <returns>
/// </returns>
FUNCTION SetDecimal(nDec AS DWORD) AS DWORD
	SETSTATE DWORD Set.Decimals nDec

///
/// Return the setting that determines the decimal separation character to be used in numeric-to-string conversion functions.
///
/// <returns>
/// </returns>

FUNCTION SetDecimalSep() AS DWORD
	GETSTATE DWORD Set.DecimalSep

///
/// Return and change the setting that determines the decimal separation character to be used in numeric-to-string conversion functions.
///
/// <param name="wSep"></param>
/// <returns>
/// </returns>
FUNCTION SetDecimalSep(wSep AS DWORD) AS DWORD
	SETSTATE DWORD Set.DecimalSep wSep

///
/// Return the setting that determines the default drive and directory.
///
/// <param name="cDefault"></param>
/// <returns>
/// </returns>
FUNCTION SetDefault() AS STRING
	GETSTATE STRING Set.Default

///
/// Change the setting that determines the default drive and directory.
///
/// <param name="cDefault"></param>
/// <returns>
/// </returns>
FUNCTION SetDefault(cDefault AS STRING) AS STRING
	SetPathArray(NULL)
	SETSTATE STRING Set.Default cDefault

///
/// Return the setting that determines whether to ignore or include records that are marked for deletion.
///
/// <returns>
/// </returns>
FUNCTION SetDeleted() AS LOGIC
	GETSTATE LOGIC Set.Deleted

///
/// Change the setting that determines whether to ignore or include records that are marked for deletion.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetDeleted(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Deleted lSet

///
/// Return the setting that determines the number of digits that will be shown to the left of the decimal point when a number is displayed.
///
/// <returns>
/// </returns>
FUNCTION SetDigit() AS DWORD
	GETSTATE DWORD Set.DIGITS

///
/// Change the setting that determines the number of digits that will be shown to the left of the decimal point when a number is displayed.
///
/// <param name="nDig"></param>
/// <returns>
/// </returns>
FUNCTION SetDigit(nDig AS DWORD) AS DWORD
	SETSTATE DWORD Set.DIGITS nDIg

///
/// Return the setting that fixes the number of digits used to display numeric output.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetDigitFixed() AS LOGIC
	GETSTATE LOGIC Set.DigitFixed

///
/// Change the setting that fixes the number of digits used to display numeric output.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetDigitFixed(f AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.DigitFixed f

///
/// Return the setting that determines how dates without century digits are interpreted.
///
/// <returns>
/// </returns>
FUNCTION SetEpoch() AS DWORD
	GETSTATE DWORD Set.Epoch

///
/// Change the setting that determines how dates without century digits are interpreted.
///
/// <param name="wYear"></param>
/// <returns>
/// </returns>
FUNCTION SetEpoch(wEpoch AS DWORD) AS DWORD
	LOCAL wYear AS DWORD
	LOCAL wCent AS DWORD
	wYear := wEpoch % 100
	wCent := (( wEpoch / 100) +1) * 100
	XSharp.RuntimeState.SetValue<DWORD> (Set.EpochYear, wYear)
	XSharp.RuntimeState.SetValue<DWORD> (Set.EpochCent, wCent)
	SETSTATE DWORD Set.Epoch	 wEpoch

///
/// Return the setting that determines whether error information is written to the error log file by the default runtime error handler.
///
/// <returns>
/// </returns>
FUNCTION SetErrorLog() AS LOGIC
	GETSTATE LOGIC Set.ERRRORLOG

///
/// Change the setting that determines whether error information is written to the error log file by the default runtime error handler.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetErrorLog(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.ERRRORLOG lSet

///
/// Return the setting for an exact match for character string comparisons.
///
/// <returns>
/// </returns>
FUNCTION SetExact() AS LOGIC
	GETSTATE LOGIC Set.Exact

///
/// Change the setting for an exact match for character string comparisons.
///
/// <param name="fExact"></param>
/// <returns>
/// </returns>
FUNCTION SetExact(fExact AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Exact fExact

///
/// Return the setting that determines whether to open database files in exclusive or shared mode.
///
/// <returns>
/// </returns>
FUNCTION SetExclusive() AS LOGIC
	GETSTATE LOGIC Set.Exclusive

///
/// Change the setting that determines whether to open database files in exclusive or shared mode.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetExclusive(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Exclusive lSet

///
/// Return the setting that determines whether assignments are made to fields or to memory variables.
///
/// <returns>
/// </returns>
FUNCTION SetFieldStore() AS LOGIC
	GETSTATE LOGIC Set.FieldStore

///
/// Change the setting that determines whether assignments are made to fields or to memory variables.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetFieldStore(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.FieldStore lSet

///
/// Return the setting that fixes the number of decimal digits used to display numbers.
///
/// <returns>
/// </returns>
FUNCTION SetFixed() AS LOGIC
	GETSTATE LOGIC Set.Fixed

///
/// Change the setting that fixes the number of decimal digits used to display numbers.
///
/// <param name="fFixed"></param>
/// <returns>
/// </returns>
FUNCTION SetFixed(fFixed AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Fixed fFixed

///
/// Return the setting that determines the internal operational characteristics of the underlying floating-point system.
///
/// <returns>
/// </returns>
FUNCTION SetMath() AS DWORD
	GETSTATE DWORD Set.MATH

///
/// Change the setting that determines the internal operational characteristics of the underlying floating-point system.
///
/// <param name="nFPU"></param>
/// <returns>
/// </returns>
FUNCTION SetMath(nFPU AS DWORD) AS DWORD
	SETSTATE DWORD Set.MATH nFPU

///
/// Activate a new DLL for nation-dependent operations and messages.
///
/// <returns>
/// </returns>
/// <remarks>
/// In Visual Objects the nation DLL is an external DLL. For X# there are no separate
/// nation DLL. The nation support code is integrated in XSharp.Core.DLL.
/// For compatibility you can still use the SetNatDLL() function to switch to another
/// nation module.

/// The available nation modules are (just like in Visual Objects):

/// 	"BRAZIL", "CROATIA", "CZECH852", "CZECH895", "DANISH", "DUTCH", "FINNISH",
/// 	"FRENCH", "GENERIC", "GERMAN", "GERMAN2", "HUNG852", "HUNGCWI", "ITALIAN",
///     "NORWEGN", "POL-ISO", "POL-MAZ", "POL852", "PORT850", "PORT860", "ROMANIA",
/// 	"RUSSIAN", "SERBIA", "SL-W-95", "SL-W-AS7", "SL-W-EE", "SLOV852", "SLOV895",
/// 	"SPANISH", "SWEDISH", "UK"
///
 You may specify a DLL name with full path. The Runtime will strip the path and extension
/// from the file name and will look for one of the names listed above.

/// The nation DLL name is NOT case sensitive.
///</remarks>

FUNCTION SetNatDLL(cNewDLL AS STRING) AS LOGIC
	LOCAL cBase AS STRING
	_SetNatDLL(cnewDLL)
	cBase := System.IO.Path.GetFileNameWithoutExtension(cNewDLL)
	_SetCollation(cBase)
	RETURN String.Compare(Messages.CurrentLanguageName, cBase, TRUE) == 0

/// <exclude/>
FUNCTION _SetCollation(cBase AS STRING) AS LOGIC
	VAR rm := System.Resources.ResourceManager{ "XSharp.Collations", TYPEOF(Functions):Assembly }
	VAR obj := rm:GetObject(cBase:ToUpper())
	IF obj != NULL
		VAR bytes := obj ASTYPE BYTE[]
		IF bytes != NULL
			XSharp.RuntimeState.CollationTable := bytes
			RETURN TRUE
		ENDIF
	ENDIF
	RETURN FALSE
/// <exclude/>
INTERNAL FUNCTION	_SetNatDLL(cNewDLL AS STRING) AS STRING
	LOCAL cBase AS STRING
	cBase := System.IO.Path.GetFileNameWithoutExtension(cNewDLL)
	Messages.SetCurrentLanguage(cBase)
	SETSTATE STRING Set.NatDLL cNewDLL

///
/// Return the setting that determines the search path for opening files. This may be a semi colon separated list of folders.
///
/// <returns>
/// </returns>
FUNCTION SetPath() AS STRING
	GETSTATE STRING Set.Path

///
/// Change the setting that determines the search path for opening files.
///
/// <param name="cPath">New path. This may be a semi colon separated list of folders.</param>
/// <returns>
/// </returns>
FUNCTION SetPath(cPath AS STRING) AS STRING
	SetPathArray(NULL)
	SETSTATE STRING Set.Path cPath

///
/// Return the Path array that is used by the File() function to locate files outside of the current directory.
/// This is a combination of the SetDefault() and SetPath() variables
/// This may be null if the file function has never been called or never been called for files outside of the current
/// directory.
///
/// <returns>
/// </returns>
FUNCTION SetPathArray() AS STRING[]
	GETSTATE STRING[] Set.PathArray

///
/// Set the Path array that is used by the File() function to locate files outside of the current directory.
/// This is a combination of the SetDefault() and SetPath() variables
/// This array gets cleared when SetPath() or SetDefault() is called and is initialized the first time File() or a related function
//  is called after the path has been changed.
///
/// <param name="aPath"></param>
/// <returns>
/// </returns>
FUNCTION SetPathArray(aPath AS STRING[]) AS STRING[]
	SETSTATE STRING[] Set.PathArray aPath

///
/// Returns a string representing the evening extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetPMExt() AS STRING
	GETSTATE STRING Set.PmExt

///
/// Set the evening extension for time strings in 12-hour format.
///
/// <param name="cExt"></param>
/// <returns>
/// </returns>
FUNCTION SetPMExt(cExt AS STRING) AS STRING
	SETSTATE STRING Set.PmExt cExt

///
/// Return the setting that displays numbers in scientific notation.
///
/// <returns>
/// </returns>
FUNCTION SetScience() AS LOGIC
	GETSTATE LOGIC Set.Science

///
/// Change the setting that displays numbers in scientific notation.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetScience(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Science lSet

///
/// Return the setting that determines whether a seek operation will find a close match when no exact match is found.
///
/// <returns>
/// </returns>
FUNCTION SetSoftSeek() AS LOGIC
	GETSTATE LOGIC Set.SoftSeek

///
/// Change the setting that determines whether a seek operation will find a close match when no exact match is found.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetSoftSeek(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.SoftSeek lSet

///
/// Return the setting that determines the thousands separation character to be used in numeric-to-string conversion functions.
///
/// <returns>
/// </returns>
FUNCTION SetThousandSep() AS DWORD
	GETSTATE DWORD Set.ThousandSep

///
/// Change the setting that determines the thousands separation character to be used in numeric-to-string conversion functions.
///
/// <param name="wSep"></param>
/// <returns>
/// </returns>
FUNCTION SetThousandSep(wSep AS DWORD) AS DWORD
	SETSTATE DWORD Set.ThousandSep wSep

///
/// Return the setting that determines the separation character to be used in time strings.
///
/// <returns>
/// </returns>
FUNCTION SetTimeSep() AS DWORD
	GETSTATE DWORD Set.TimeSep

///
/// Change the setting that determines the separation character to be used in time strings.
///
/// <param name="dwChar"></param>
/// <returns>
/// </returns>
FUNCTION SetTimeSep(dwChar AS DWORD) AS DWORD
	SETSTATE DWORD Set.TimeSep dwChar

///
/// Return the setting that determines whether to include unique record keys in an order.
///
/// <returns>
/// </returns>

FUNCTION SetUnique() AS LOGIC
	GETSTATE LOGIC Set.Unique

///
/// Change the setting that determines whether to include unique record keys in an order.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>

FUNCTION SetUnique(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Unique lSet

///
///
/// <returns>
/// </returns>
FUNCTION SetYield() AS LOGIC
	GETSTATE LOGIC Set.Yield

///
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetYield(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Yield lSet

///
/// Retrieve and set the X# return code.
///
/// <param name="dw"></param>
/// <returns>
/// </returns>
FUNCTION ErrorLevel(dw AS DWORD) AS DWORD
	SETSTATE DWORD Set.ErrorLevel dw

///
/// Retrieve the X# return code.
///
/// <returns>
/// </returns>
FUNCTION ErrorLevel() AS DWORD
	GETSTATE DWORD Set.ErrorLevel

///
/// Return the setting that determines the international mode for the application
///
/// <returns>The current setting, either "Windows" (the default) or  "Clipper"
/// </returns>
FUNCTION SetInternational() AS STRING
	RETURN RuntimeState.International:ToString():ToUpper()

///
/// Return and change the setting that determines the international mode for the application
///
// <param name="cMode">The collation mode to use. The available modes are "Windows" (the default) and "Clipper". "Unicode" and "Ordinal" can be used as synonym for "Windows".</param>
/// <returns>The current setting, either "Windows" (the default) or  "Clipper"
/// </returns>
/// <remarks>
/// SetInternational() allows XSharp apps to operate in different international modes.
/// The "CLIPPER" mode is provided for compatibility with CA-Clipper applications and uses an
/// internationalization routine defined in the nation module.
/// The "Windows" mode uses international services provided by Windows.
/// When you set this mode several settings will be changed
/// <list type="table">
/// <listheader>
/// <term>Setting</term> <description>Initial value in CLIPPER mode</description>
/// </listheader>
///	<item><term>SetAmExt</term> <description>Empty String</description></item>
/// <item><term>SetPmExt</term> <description>Empty String</description></item>
/// <item><term>SetAmPm</term> <description>FALSE (24 hour format)</description></item>
/// <item><term>SetCentury</term> <description>FALSE</description></item>
/// <item><term>SetDateCountry</term> <description>American (1)</description></item>
/// <item><term>SetDateFormat</term> <description>mm/dd/yy</description></item>
/// <item><term>SetDecimal</term> <description>2</description></item>
/// <item><term>SetDecimalSep</term> <description>Period (.)</description></item>
/// <item><term>SetThousandSep</term> <description>Comma (,)</description></item>
/// <item><term>SetTimeSep</term> <description>Colon(:)</description></item>
/// </list>
/// </remarks>
FUNCTION SetInternational(cMode AS STRING) AS STRING
	LOCAL cOld AS STRING
	cOld := RuntimeState.International:ToString():ToUpper()
	SWITCH cMode:ToUpper()
	CASE "CLIPPER"
		RuntimeState.GetInstance()._SetInternationalClipper()
	CASE "WINDOWS"
	CASE "UNICODE"
	CASE "ORDINAL"
		RuntimeState.GetInstance()._SetInternationalWindows()
	OTHERWISE
		THROW Error.ArgumentError(__ENTITY__, NAMEOF(cMode), "Unsupported international mode: "+ cMode)
	END SWITCH
	RETURN cOld

///
/// Return and change the setting that determines the internal collation routine used for string comparisons when running in the VO or Vulcan dialect.
/// The Core dialect always compares according to the Unicode rules.
/// The available modes are "Windows" (the default),  "Clipper", "Unicode" and "Ordinal".
///
/// <returns>The current setting for SetCollation().</returns>
FUNCTION SetCollation() AS STRING
	RETURN RuntimeState.CollationMode:ToString():ToUpper()

///
/// Return and change the setting that determines the internal collation routine used for string comparisons when running in the VO or Vulcan dialect.
///
/// <remarks>
/// The Core dialect always compares according to the Unicode rules.
/// The other dialects use the rules defined with SetCollation for string comparison. There are 4 possible values:
/// <list type="table">
/// <item>
/// <term>Windows (the default)</term>
/// <description>This uses the normal windows Ansi comparison mechanism that Visual Objects uses as well.
/// That means that the Unicode strings from .Net are converted to Ansi first and then compared with the Ansi comparison rules.
///
/// In most cases characters that are not available in the Ansi codepage are translated to a question mark '?' and are therefore all seen as equal.

/// If you want true unicode comparison you need the Unicode value for SetCollation.
/// </description>
/// </item>
/// <item>
/// <term>Clipper</term>
/// <description>This uses string comparison tables that are the same as the character comparison tables in the Visual Objects
/// nation modules. Each character from the unicode string is converted to a character from the OEM codepage first using the
/// DosCodePage from the runtime state. the resulting OEM characters are then looked up in the 256 character weight tables that
/// are part of the runtime dll. You can switch to a different table by using SetNatDLL().
/// </description>
/// </item>
/// <item>
/// <term>Unicode</term>
/// <description>This uses the normal Unicode String.Compare routines for string comparisons.
/// </description>
/// </item>
/// <item>
/// <term>Ordinal</term>
/// <description>This uses the normal Ordinal String.Compare routines from DotNet. This is the fastest.
/// </description>
/// </item>
/// </list>
/// </remarks>
/// <returns>The current setting, either "Windows" (the default),  "Clipper", "Unicode" or "Ordinal" </returns>
/// <param name="cCollation">The collation mode to use. The available modes are "Windows" (the default),  "Clipper", "Unicode" and "Ordinal". </param>
FUNCTION SetCollation(cCollation AS STRING)  AS STRING
	LOCAL cOld AS STRING
	cOld := RuntimeState.CollationMode:ToString():ToUpper()
	SWITCH cCollation:ToUpper()
	CASE "CLIPPER"
		RuntimeState.CollationMode := CollationMode.Clipper
	CASE "WINDOWS"
		RuntimeState.CollationMode := CollationMode.Windows
	CASE "UNICODE"
		RuntimeState.CollationMode := CollationMode.Unicode
	CASE "ORDINAL"
		RuntimeState.CollationMode := CollationMode.Ordinal
	OTHERWISE
		THROW Error.ArgumentError(__ENTITY__, NAMEOF(cCollation), "Unsupported collation mode: "+cCollation)
	END SWITCH
	RETURN cOld

//
// 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.
//
// Most of these settings will Get and Set properties of the Runtime.State class
#include "GetSet.xh"

USING XSharp

///
/// Returns a string representing the morning extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION GetAMExt() AS STRING
	GETSTATE STRING Set.AmExt

///
/// Returns a string representing the morning extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetAMExt() AS STRING
	GETSTATE STRING Set.AmExt

///
/// Set the morning extension for time strings in 12-hour format.
///
/// <param name="cExt"></param>
/// <returns>
/// </returns>
FUNCTION SetAMExt(cExt AS STRING) AS STRING
	SETSTATE STRING Set.AmExt cExt

///
/// Returns the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <returns>
/// </returns>
FUNCTION GetAmPm() AS LOGIC
	GETSTATE LOGIC Set.AmPm

///
/// Returns the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetAmPm() AS LOGIC
	GETSTATE LOGIC Set.AmPm

///
/// Change the setting that determines whether time strings are in 12-hour or 24-hour format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetAmPm(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.AmPm lSet

///
/// Return and the setting that determines whether database files are created using ANSI or OEM format and whether certain text file operations convert between the two character sets.
///
/// <returns>
/// </returns>
FUNCTION SetAnsi() AS LOGIC
	RETURN RuntimeState.Ansi

///
/// Change the setting that determines whether database files are created using ANSI or OEM format and whether certain text file operations convert between the two character sets.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetAnsi(lSet AS LOGIC) AS LOGIC
	LOCAL lOld := RuntimeState.Ansi AS LOGIC
	RuntimeState.Ansi := lSet
	RETURN lOld

///
/// Return the setting that determines whether a beep is sounded by the error system when an error occurs.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetBeep() AS LOGIC
	GETSTATE LOGIC Set.BELL

///
/// Change the setting that determines whether a beep is sounded by the error system when an error occurs.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetBeep(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.BELL lSet

///
/// Return the setting that determines whether to include or omit century digits in the date format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetCentury() AS LOGIC
	GETSTATE LOGIC Set.Century

///
/// Change the setting that determines whether to include or omit century digits in the date format.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetCentury(lSet AS LOGIC) AS LOGIC
	LOCAL lOld AS LOGIC
    lOld := XSharp.RuntimeState.GetValue<LOGIC>(Set.CENTURY)
    IF lOld != lSet
      VAR cFormat := XSharp.RuntimeState.DateFormat

      IF lSet
        IF ! cFormat:Contains("YYYY")
            cFormat := cFormat:Replace( "YY", "YYYY" )
        ENDIF
        ELSE
            IF cFormat:Contains("YYYY")
                cFormat := cFormat:Replace( "YYYY", "YY" )
            ENDIF
      ENDIF

      SetDateFormat( cFormat )

    ENDIF
    RETURN lOld

///
/// Return the setting that determines the type of central processor you have.
///
/// <param name="nCpu"></param>
/// <returns>
/// </returns>
FUNCTION SetCpu() AS DWORD
	GETSTATE DWORD Set.CPU

///
/// Change the setting that determines the type of central processor you have.
///
/// <param name="nCpu"></param>
/// <returns>
/// </returns>
FUNCTION SetCpu(nCpu AS DWORD) AS DWORD
	SETSTATE DWORD Set.CPU nCPU

///
/// Return the setting that determines the X# date format by selecting from a list of constants with corresponding date formats.
///
/// <param name="dwCountry"></param>
/// <returns>
/// </returns>
FUNCTION SetDateCountry() AS DWORD
	GETSTATE DWORD Set.DATECOUNTRY

///
/// Return and optionally change the setting that determines the X# date format by selecting from a list of constants with corresponding date formats.
///
/// <param name="dwCountry"></param>
/// <returns>
/// </returns>
FUNCTION SetDateCountry(dwCountry AS DWORD) AS DWORD
//	setstate DWORD Set.DATECOUNTRY dwCountry
	LOCAL dwOld := RuntimeState.DateCountry AS DWORD
	RuntimeState.DateCountry := dwCountry
	RETURN dwOld

///
/// Return and optionally change the setting that determines the X# date format by selecting from a list of constants with corresponding date formats.
///
/// <param name="enumCountry"></param>
/// <returns>
/// </returns>
FUNCTION SetDateCountry( enumCountry AS DateCountry ) AS DWORD
    RETURN SetDateCountry( (DWORD) enumCountry )

///
/// Return the current Date format.
///
/// <returns>
/// </returns>
FUNCTION GetDateFormat() AS STRING
	GETSTATE STRING Set.DateFormat

///
/// Change the setting that determines the X# date format.
///
/// <param name="cDateFormat"></param>
/// <returns>
/// </returns>
FUNCTION SetDateFormat(cDateFormat AS STRING) AS STRING
	LOCAL cOld AS STRING
	// Changing Dateformat also changes DateCountry and Century
	cOld := RuntimeState.DateFormat
	RuntimeState.DateFormat := cDateFormat
	RETURN cOld

///
/// Return the setting that determines the number of decimal places used to display numbers.
///
/// <returns>
/// </returns>
FUNCTION SetDecimal() AS DWORD
	GETSTATE DWORD Set.Decimals

///
/// Return and change the setting that determines the number of decimal places used to display numbers.
///
/// <param name="nDec"></param>
/// <returns>
/// </returns>
FUNCTION SetDecimal(nDec AS DWORD) AS DWORD
	SETSTATE DWORD Set.Decimals nDec

///
/// Return the setting that determines the decimal separation character to be used in numeric-to-string conversion functions.
///
/// <returns>
/// </returns>

FUNCTION SetDecimalSep() AS DWORD
	GETSTATE DWORD Set.DecimalSep

///
/// Return and change the setting that determines the decimal separation character to be used in numeric-to-string conversion functions.
///
/// <param name="wSep"></param>
/// <returns>
/// </returns>
FUNCTION SetDecimalSep(wSep AS DWORD) AS DWORD
	SETSTATE DWORD Set.DecimalSep wSep

///
/// Return the setting that determines the default drive and directory.
///
/// <param name="cDefault"></param>
/// <returns>
/// </returns>
FUNCTION SetDefault() AS STRING
	GETSTATE STRING Set.Default

///
/// Change the setting that determines the default drive and directory.
///
/// <param name="cDefault"></param>
/// <returns>
/// </returns>
FUNCTION SetDefault(cDefault AS STRING) AS STRING
	SetPathArray(NULL)
	SETSTATE STRING Set.Default cDefault

///
/// Return the setting that determines whether to ignore or include records that are marked for deletion.
///
/// <returns>
/// </returns>
FUNCTION SetDeleted() AS LOGIC
	GETSTATE LOGIC Set.Deleted

///
/// Change the setting that determines whether to ignore or include records that are marked for deletion.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetDeleted(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Deleted lSet

///
/// Return the setting that determines the number of digits that will be shown to the left of the decimal point when a number is displayed.
///
/// <returns>
/// </returns>
FUNCTION SetDigit() AS DWORD
	GETSTATE DWORD Set.DIGITS

///
/// Change the setting that determines the number of digits that will be shown to the left of the decimal point when a number is displayed.
///
/// <param name="nDig"></param>
/// <returns>
/// </returns>
FUNCTION SetDigit(nDig AS DWORD) AS DWORD
	SETSTATE DWORD Set.DIGITS nDIg

///
/// Return the setting that fixes the number of digits used to display numeric output.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetDigitFixed() AS LOGIC
	GETSTATE LOGIC Set.DigitFixed

///
/// Change the setting that fixes the number of digits used to display numeric output.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetDigitFixed(f AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.DigitFixed f

///
/// Return the setting that determines how dates without century digits are interpreted.
///
/// <returns>
/// </returns>
FUNCTION SetEpoch() AS DWORD
	GETSTATE DWORD Set.Epoch

///
/// Change the setting that determines how dates without century digits are interpreted.
///
/// <param name="wYear"></param>
/// <returns>
/// </returns>
FUNCTION SetEpoch(wEpoch AS DWORD) AS DWORD
	LOCAL wYear AS DWORD
	LOCAL wCent AS DWORD
	wYear := wEpoch % 100
	wCent := (( wEpoch / 100) +1) * 100
	XSharp.RuntimeState.SetValue<DWORD> (Set.EpochYear, wYear)
	XSharp.RuntimeState.SetValue<DWORD> (Set.EpochCent, wCent)
	SETSTATE DWORD Set.Epoch	 wEpoch

///
/// Return the setting that determines whether error information is written to the error log file by the default runtime error handler.
///
/// <returns>
/// </returns>
FUNCTION SetErrorLog() AS LOGIC
	GETSTATE LOGIC Set.ERRRORLOG

///
/// Change the setting that determines whether error information is written to the error log file by the default runtime error handler.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetErrorLog(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.ERRRORLOG lSet

///
/// Return the setting for an exact match for character string comparisons.
///
/// <returns>
/// </returns>
FUNCTION SetExact() AS LOGIC
	GETSTATE LOGIC Set.Exact

///
/// Change the setting for an exact match for character string comparisons.
///
/// <param name="fExact"></param>
/// <returns>
/// </returns>
FUNCTION SetExact(fExact AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Exact fExact

///
/// Return the setting that determines whether to open database files in exclusive or shared mode.
///
/// <returns>
/// </returns>
FUNCTION SetExclusive() AS LOGIC
	GETSTATE LOGIC Set.Exclusive

///
/// Change the setting that determines whether to open database files in exclusive or shared mode.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetExclusive(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Exclusive lSet

///
/// Return the setting that determines whether assignments are made to fields or to memory variables.
///
/// <returns>
/// </returns>
FUNCTION SetFieldStore() AS LOGIC
	GETSTATE LOGIC Set.FieldStore

///
/// Change the setting that determines whether assignments are made to fields or to memory variables.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetFieldStore(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.FieldStore lSet

///
/// Return the setting that fixes the number of decimal digits used to display numbers.
///
/// <returns>
/// </returns>
FUNCTION SetFixed() AS LOGIC
	GETSTATE LOGIC Set.Fixed

///
/// Change the setting that fixes the number of decimal digits used to display numbers.
///
/// <param name="fFixed"></param>
/// <returns>
/// </returns>
FUNCTION SetFixed(fFixed AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Fixed fFixed

///
/// Return the setting that determines the internal operational characteristics of the underlying floating-point system.
///
/// <returns>
/// </returns>
FUNCTION SetMath() AS DWORD
	GETSTATE DWORD Set.MATH

///
/// Change the setting that determines the internal operational characteristics of the underlying floating-point system.
///
/// <param name="nFPU"></param>
/// <returns>
/// </returns>
FUNCTION SetMath(nFPU AS DWORD) AS DWORD
	SETSTATE DWORD Set.MATH nFPU

///
/// Activate a new DLL for nation-dependent operations and messages.
///
/// <returns>
/// </returns>
/// <remarks>
/// In Visual Objects the nation DLL is an external DLL. For X# there are no separate
/// nation DLL. The nation support code is integrated in XSharp.Core.DLL.
/// For compatibility you can still use the SetNatDLL() function to switch to another
/// nation module.

/// The available nation modules are (just like in Visual Objects):

/// 	"BRAZIL", "CROATIA", "CZECH852", "CZECH895", "DANISH", "DUTCH", "FINNISH",
/// 	"FRENCH", "GENERIC", "GERMAN", "GERMAN2", "HUNG852", "HUNGCWI", "ITALIAN",
///     "NORWEGN", "POL-ISO", "POL-MAZ", "POL852", "PORT850", "PORT860", "ROMANIA",
/// 	"RUSSIAN", "SERBIA", "SL-W-95", "SL-W-AS7", "SL-W-EE", "SLOV852", "SLOV895",
/// 	"SPANISH", "SWEDISH", "UK"
///
 You may specify a DLL name with full path. The Runtime will strip the path and extension
/// from the file name and will look for one of the names listed above.

/// The nation DLL name is NOT case sensitive.
///</remarks>

FUNCTION SetNatDLL(cNewDLL AS STRING) AS LOGIC
	LOCAL cBase AS STRING
	_SetNatDLL(cnewDLL)
	cBase := System.IO.Path.GetFileNameWithoutExtension(cNewDLL)
	_SetCollation(cBase)
	RETURN String.Compare(Messages.CurrentLanguageName, cBase, TRUE) == 0

/// <exclude/>
FUNCTION _SetCollation(cBase AS STRING) AS LOGIC
	VAR rm := System.Resources.ResourceManager{ "XSharp.Collations", TYPEOF(Functions):Assembly }
	VAR obj := rm:GetObject(cBase:ToUpper())
	IF obj != NULL
		VAR bytes := obj ASTYPE BYTE[]
		IF bytes != NULL
			XSharp.RuntimeState.CollationTable := bytes
			RETURN TRUE
		ENDIF
	ENDIF
	RETURN FALSE
/// <exclude/>
INTERNAL FUNCTION	_SetNatDLL(cNewDLL AS STRING) AS STRING
	LOCAL cBase AS STRING
	cBase := System.IO.Path.GetFileNameWithoutExtension(cNewDLL)
	Messages.SetCurrentLanguage(cBase)
	SETSTATE STRING Set.NatDLL cNewDLL

///
/// Return the setting that determines the search path for opening files. This may be a semi colon separated list of folders.
///
/// <returns>
/// </returns>
FUNCTION SetPath() AS STRING
	GETSTATE STRING Set.Path

///
/// Change the setting that determines the search path for opening files.
///
/// <param name="cPath">New path. This may be a semi colon separated list of folders.</param>
/// <returns>
/// </returns>
FUNCTION SetPath(cPath AS STRING) AS STRING
	SetPathArray(NULL)
	SETSTATE STRING Set.Path cPath

///
/// Return the Path array that is used by the File() function to locate files outside of the current directory.
/// This is a combination of the SetDefault() and SetPath() variables
/// This may be null if the file function has never been called or never been called for files outside of the current
/// directory.
///
/// <returns>
/// </returns>
FUNCTION SetPathArray() AS STRING[]
	GETSTATE STRING[] Set.PathArray

///
/// Set the Path array that is used by the File() function to locate files outside of the current directory.
/// This is a combination of the SetDefault() and SetPath() variables
/// This array gets cleared when SetPath() or SetDefault() is called and is initialized the first time File() or a related function
//  is called after the path has been changed.
///
/// <param name="aPath"></param>
/// <returns>
/// </returns>
FUNCTION SetPathArray(aPath AS STRING[]) AS STRING[]
	SETSTATE STRING[] Set.PathArray aPath

///
/// Returns a string representing the evening extension for time strings in 12-hour format.
///
/// <returns>
/// </returns>
FUNCTION SetPMExt() AS STRING
	GETSTATE STRING Set.PmExt

///
/// Set the evening extension for time strings in 12-hour format.
///
/// <param name="cExt"></param>
/// <returns>
/// </returns>
FUNCTION SetPMExt(cExt AS STRING) AS STRING
	SETSTATE STRING Set.PmExt cExt

///
/// Return the setting that displays numbers in scientific notation.
///
/// <returns>
/// </returns>
FUNCTION SetScience() AS LOGIC
	GETSTATE LOGIC Set.Science

///
/// Change the setting that displays numbers in scientific notation.
///
/// <param name="f"></param>
/// <returns>
/// </returns>
FUNCTION SetScience(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Science lSet

///
/// Return the setting that determines whether a seek operation will find a close match when no exact match is found.
///
/// <returns>
/// </returns>
FUNCTION SetSoftSeek() AS LOGIC
	GETSTATE LOGIC Set.SoftSeek

///
/// Change the setting that determines whether a seek operation will find a close match when no exact match is found.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetSoftSeek(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.SoftSeek lSet

///
/// Return the setting that determines the thousands separation character to be used in numeric-to-string conversion functions.
///
/// <returns>
/// </returns>
FUNCTION SetThousandSep() AS DWORD
	GETSTATE DWORD Set.ThousandSep

///
/// Change the setting that determines the thousands separation character to be used in numeric-to-string conversion functions.
///
/// <param name="wSep"></param>
/// <returns>
/// </returns>
FUNCTION SetThousandSep(wSep AS DWORD) AS DWORD
	SETSTATE DWORD Set.ThousandSep wSep

///
/// Return the setting that determines the separation character to be used in time strings.
///
/// <returns>
/// </returns>
FUNCTION SetTimeSep() AS DWORD
	GETSTATE DWORD Set.TimeSep

///
/// Change the setting that determines the separation character to be used in time strings.
///
/// <param name="dwChar"></param>
/// <returns>
/// </returns>
FUNCTION SetTimeSep(dwChar AS DWORD) AS DWORD
	SETSTATE DWORD Set.TimeSep dwChar

///
/// Return the setting that determines whether to include unique record keys in an order.
///
/// <returns>
/// </returns>

FUNCTION SetUnique() AS LOGIC
	GETSTATE LOGIC Set.Unique

///
/// Change the setting that determines whether to include unique record keys in an order.
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>

FUNCTION SetUnique(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Unique lSet

///
///
/// <returns>
/// </returns>
FUNCTION SetYield() AS LOGIC
	GETSTATE LOGIC Set.Yield

///
///
/// <param name="lSet"></param>
/// <returns>
/// </returns>
FUNCTION SetYield(lSet AS LOGIC) AS LOGIC
	SETSTATE LOGIC Set.Yield lSet

///
/// Retrieve and set the X# return code.
///
/// <param name="dw"></param>
/// <returns>
/// </returns>
FUNCTION ErrorLevel(dw AS DWORD) AS DWORD
	SETSTATE DWORD Set.ErrorLevel dw

///
/// Retrieve the X# return code.
///
/// <returns>
/// </returns>
FUNCTION ErrorLevel() AS DWORD
	GETSTATE DWORD Set.ErrorLevel

///
/// Return the setting that determines the international mode for the application
///
/// <returns>The current setting, either "Windows" (the default) or  "Clipper"
/// </returns>
FUNCTION SetInternational() AS STRING
	RETURN RuntimeState.International:ToString():ToUpper()

///
/// Return and change the setting that determines the international mode for the application
///
// <param name="cMode">The collation mode to use. The available modes are "Windows" (the default) and "Clipper". "Unicode" and "Ordinal" can be used as synonym for "Windows".</param>
/// <returns>The current setting, either "Windows" (the default) or  "Clipper"
/// </returns>
/// <remarks>
/// SetInternational() allows XSharp apps to operate in different international modes.
/// The "CLIPPER" mode is provided for compatibility with CA-Clipper applications and uses an
/// internationalization routine defined in the nation module.
/// The "Windows" mode uses international services provided by Windows.
/// When you set this mode several settings will be changed
/// <list type="table">
/// <listheader>
/// <term>Setting</term> <description>Initial value in CLIPPER mode</description>
/// </listheader>
///	<item><term>SetAmExt</term> <description>Empty String</description></item>
/// <item><term>SetPmExt</term> <description>Empty String</description></item>
/// <item><term>SetAmPm</term> <description>FALSE (24 hour format)</description></item>
/// <item><term>SetCentury</term> <description>FALSE</description></item>
/// <item><term>SetDateCountry</term> <description>American (1)</description></item>
/// <item><term>SetDateFormat</term> <description>mm/dd/yy</description></item>
/// <item><term>SetDecimal</term> <description>2</description></item>
/// <item><term>SetDecimalSep</term> <description>Period (.)</description></item>
/// <item><term>SetThousandSep</term> <description>Comma (,)</description></item>
/// <item><term>SetTimeSep</term> <description>Colon(:)</description></item>
/// </list>
/// </remarks>
FUNCTION SetInternational(cMode AS STRING) AS STRING
	LOCAL cOld AS STRING
	cOld := RuntimeState.International:ToString():ToUpper()
	SWITCH cMode:ToUpper()
	CASE "CLIPPER"
		RuntimeState.GetInstance()._SetInternationalClipper()
	CASE "WINDOWS"
	CASE "UNICODE"
	CASE "ORDINAL"
		RuntimeState.GetInstance()._SetInternationalWindows()
	OTHERWISE
		THROW Error.ArgumentError(__ENTITY__, NAMEOF(cMode), "Unsupported international mode: "+ cMode)
	END SWITCH
	RETURN cOld

///
/// Return and change the setting that determines the internal collation routine used for string comparisons when running in the VO or Vulcan dialect.
/// The Core dialect always compares according to the Unicode rules.
/// The available modes are "Windows" (the default),  "Clipper", "Unicode" and "Ordinal".
///
/// <returns>The current setting for SetCollation().</returns>
FUNCTION SetCollation() AS STRING
	RETURN RuntimeState.CollationMode:ToString():ToUpper()

///
/// Return and change the setting that determines the internal collation routine used for string comparisons when running in the VO or Vulcan dialect.
///
/// <remarks>
/// The Core dialect always compares according to the Unicode rules.
/// The other dialects use the rules defined with SetCollation for string comparison. There are 4 possible values:
/// <list type="table">
/// <item>
/// <term>Windows (the default)</term>
/// <description>This uses the normal windows Ansi comparison mechanism that Visual Objects uses as well.
/// That means that the Unicode strings from .Net are converted to Ansi first and then compared with the Ansi comparison rules.
///
/// In most cases characters that are not available in the Ansi codepage are translated to a question mark '?' and are therefore all seen as equal.

/// If you want true unicode comparison you need the Unicode value for SetCollation.
/// </description>
/// </item>
/// <item>
/// <term>Clipper</term>
/// <description>This uses string comparison tables that are the same as the character comparison tables in the Visual Objects
/// nation modules. Each character from the unicode string is converted to a character from the OEM codepage first using the
/// DosCodePage from the runtime state. the resulting OEM characters are then looked up in the 256 character weight tables that
/// are part of the runtime dll. You can switch to a different table by using SetNatDLL().
/// </description>
/// </item>
/// <item>
/// <term>Unicode</term>
/// <description>This uses the normal Unicode String.Compare routines for string comparisons.
/// </description>
/// </item>
/// <item>
/// <term>Ordinal</term>
/// <description>This uses the normal Ordinal String.Compare routines from DotNet. This is the fastest.
/// </description>
/// </item>
/// </list>
/// </remarks>
/// <returns>The current setting, either "Windows" (the default),  "Clipper", "Unicode" or "Ordinal" </returns>
/// <param name="cCollation">The collation mode to use. The available modes are "Windows" (the default),  "Clipper", "Unicode" and "Ordinal". </param>
FUNCTION SetCollation(cCollation AS STRING)  AS STRING
	LOCAL cOld AS STRING
	cOld := RuntimeState.CollationMode:ToString():ToUpper()
	SWITCH cCollation:ToUpper()
	CASE "CLIPPER"
		RuntimeState.CollationMode := CollationMode.Clipper
	CASE "WINDOWS"
		RuntimeState.CollationMode := CollationMode.Windows
	CASE "UNICODE"
		RuntimeState.CollationMode := CollationMode.Unicode
	CASE "ORDINAL"
		RuntimeState.CollationMode := CollationMode.Ordinal
	OTHERWISE
		THROW Error.ArgumentError(__ENTITY__, NAMEOF(cCollation), "Unsupported collation mode: "+cCollation)
	END SWITCH
	RETURN cOld


[Runtime] WorkDir() now returns the folder of the EXE and not the folder of the runtime

//
// 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 XSharp
USING System.IO

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/doserror/*" />
FUNCTION DosError(nNewDosCode AS DWORD) AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	System.Environment.ExitCode := UNCHECKED((INT) nNewDosCode)
	RETURN UNCHECKED((DWORD) nOld)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/doserror/*" />
FUNCTION DosError() AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	RETURN UNCHECKED((DWORD) nOld)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getdoserror/*" />
FUNCTION GetDosError() AS DWORD
	RETURN UNCHECKED((DWORD) System.Runtime.InteropServices.Marshal.GetLastWin32Error())

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getenv/*" />
FUNCTION GetEnv(cEnvVariable AS STRING) AS STRING
	RETURN System.Environment.GetEnvironmentVariable(cEnvVariable)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/setenv/*" />

FUNCTION SetEnv(cVar AS STRING,cValue AS STRING) AS LOGIC
	RETURN SetEnv(cVar, cValue, FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/setenv/*" />
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING,lAppend AS LOGIC) AS LOGIC
	LOCAL result AS LOGIC
	TRY
        XSharp.IO.File.ClearErrorState()

		IF lAppend
			LOCAL cOldValue AS STRING
			cOldValue := System.Environment.GetEnvironmentVariable(cVar)
			IF ! String.IsNullOrEmpty( cOldValue )
				cOldValue += ";"
			ENDIF
			cValue := cOldValue + cValue
		ENDIF
		System.Environment.SetEnvironmentVariable(cVar, cValue)
		result := System.Environment.GetEnvironmentVariable(cVar) == cValue
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
		result := FALSE
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/netname/*" />

FUNCTION NetName() AS STRING
	RETURN System.Environment.MachineName

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdir/*" />
FUNCTION CurDir (cDrive AS STRING) AS STRING
	RETURN CurDir()

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdir/*" />
FUNCTION CurDir() AS STRING
	LOCAL cDir AS STRING
	LOCAL index AS INT
	cDir := System.Environment.CurrentDirectory
	index := cDir:IndexOf(Path.VolumeSeparatorChar)
	IF index > 0
		cDir := cDir:Substring(index+1)
	ENDIF
	IF cDir:Length > 0 .AND. cDir[0] == Path.DirectorySeparatorChar
		cDir := cDir:Substring(1)
	ENDIF
	IF cDir:Length > 0 .AND. cDir[cDir:Length-1]  == Path.DirectorySeparatorChar
		cDir := cDir:Substring(0, cDir:Length-1)
	ENDIF
	RETURN cDir

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdrive/*" />
FUNCTION CurDrive() AS STRING
	LOCAL currentDirectory := System.IO.Directory.GetCurrentDirectory() AS STRING
	LOCAL drive := "" AS STRING
	LOCAL position AS INT

	position := currentDirectory:IndexOf(System.IO.Path.VolumeSeparatorChar)
	IF position > 0
		drive := currentDirectory:Substring(0,position)
	ENDIF
	RETURN drive

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/workdir/*" />
FUNCTION WorkDir() AS STRING
	LOCAL cPath AS STRING
	LOCAL asm   AS System.Reflection.Assembly
	asm := System.Reflection.Assembly.GetEntryAssembly()
	cPath := asm:Location
	cPath := Path.GetDirectoryName(cPath)
	IF cPath[cPath:Length-1] !=  Path.DirectorySeparatorChar
		cPath += Path.DirectorySeparatorChar:ToString()
	ENDIF
	RETURN cPath

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskfree/*" />
FUNCTION DiskFree() AS INT64
	RETURN DiskFree(CurDrive())

INTERNAL FUNCTION DiskNo2DiskName(nDisk AS INT) AS STRING
    nDisk := _AND(nDisk, 0xFF)
    IF nDisk >= 0 .AND. nDisk <= 25
        LOCAL charDrive AS CHAR
        charDrive := (CHAR) ( 65 + (nDisk-1))
	    RETURN charDrive:ToString()
    ELSE
        RETURN CurDrive()
    ENDIF

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskfree/*" />
FUNCTION DiskFree(cDrive AS STRING) AS INT64
    LOCAL result AS INT64
    IF String.IsNullOrEmpty(cDrive)
        cDrive := CurDrive()
    ELSEIF cDrive:Length > 1
        cDrive := cDrive:Substring(0,1)+":"
    ENDIF
    TRY
        XSharp.IO.File.ClearErrorState()
	    result :=  DriveInfo{cDrive}:TotalFreeSpace
    CATCH e AS Exception
        XSharp.IO.File.SetErrorState(e)
        result := 0
    END TRY
    RETURN result

/// <inheritdoc cref="DiskFree" />
/// <param name="nDrive">The number of the disk drive to query, where 1 is drive A, 2 is B, 3 is C, and so on. </param>
FUNCTION DiskFree(nDrive AS INT) AS INT64
	RETURN DiskFree(DiskNo2DiskName(nDrive))

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskname/*" />
FUNCTION DiskName() AS STRING
	RETURN CurDrive()

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskspace/*" />
FUNCTION DiskSpace() AS INT64
	RETURN DiskSpace(CurDrive())

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskspace/*" />
FUNCTION DiskSpace(nDrive AS INT) AS INT64
	LOCAL cDisk AS STRING
	cDisk := DiskNo2DiskName(nDrive)
	RETURN DiskSpace(cDisk)

/// <inheritdoc cref="DiskSpace" />
/// <param name="cDrive">The name of the drive as a string, for example "C:", "A:".  If you do not specify a drive, the Windows default is used.</param>
FUNCTION DiskSpace(cDrive AS STRING) AS INT64
    LOCAL result AS INT64
    IF String.IsNullOrEmpty(cDrive)
        cDrive := CurDrive()
    ELSEIF cDrive:Length > 1
        cDrive := cDrive:Substring(0,1)+":"
    ENDIF
    TRY
        XSharp.IO.File.ClearErrorState()
	    result :=  DriveInfo{cDrive}:TotalSize
    CATCH e AS Exception
        XSharp.IO.File.SetErrorState(e)
        result := 0
    END TRY
    RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/neterr/*" />
FUNCTION NetErr() AS LOGIC STRICT
    RETURN RuntimeState.NetErr

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/neterr/*" />
FUNCTION NetErr( lNewError AS LOGIC ) AS LOGIC
    LOCAL curvalue := RuntimeState.NetErr AS LOGIC
    RuntimeState.NetErr := lNewError
    RETURN curvalue

/// <summary>
/// </summary>
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries() AS DWORD
	RETURN RuntimeState.LockTries

/// <summary>
/// </summary>
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries(nValue AS DWORD) AS DWORD
	LOCAL nResult AS DWORD
	nResult := RuntimeState.LockTries
	RuntimeState.LockTries := nValue
	RETURN nResult

INTERNAL FUNCTION BadFileParam(cFunction AS STRING, cParam AS STRING, nParam AS DWORD) AS VOID
    RuntimeState.FileError      := FERROR_PARAM
    RuntimeState.FileException  := Error.ArgumentError(cFunction,cParam, nParam,"Empty file or directoryname is not allowed")
    RuntimeState.NetErr         := FALSE
RETURN

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirchange/*" />
FUNCTION DirChange(pszDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszDir)
		   BadFileParam(__FUNCTION__, nameof(pszDir), 1)
		   RETURN -1
		ENDIF
		IF Directory.Exists(pszDir)
			Directory.SetCurrentDirectory(pszDir)
			result := 0
		ELSE
			result := 3 // Path not found
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirmake/*" />
FUNCTION DirMake(pszNewDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.ClearErrorState()
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszNewDir)
		   BadFileParam(__FUNCTION__, nameof(pszNewDir), 1)
		   RETURN -1
		ENDIF
		IF !Directory.Exists(pszNewDir)
			Directory.CreateDirectory(pszNewDir)
			result := 0
		ELSE
			result := 183 // ERROR_ALREADY_EXISTS
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirremove/*" />
FUNCTION DirRemove(pszDirName AS STRING) AS INT
	LOCAL result AS INT
	TRY
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszDirName)
		   BadFileParam(__FUNCTION__, nameof(pszDirName), 1)
		   RETURN -1
		ENDIF
		IF Directory.Exists(pszDirName)
			Directory.Delete(pszDirName,FALSE)
			result := 0
		ELSE
			result := 2 // Cannot find file
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskchange/*" />
FUNCTION DiskChange(pszDrive AS STRING) AS LOGIC
	IF String.IsNullOrEmpty(pszDrive)
		RETURN FALSE
	ENDIF
	pszDrive := pszDrive:Substring(0,1)+Path.VolumeSeparatorChar:ToString()+Path.DirectorySeparatorChar:ToString()
	RETURN DirChange(pszDrive) == 0

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/os/*" />
FUNCTION OS() AS STRING
	RETURN OS(FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/os/*" />
FUNCTION OS(lExtended AS LOGIC) AS STRING
    IF IsRunningOnWindows()
        RETURN ReadOsVersionFromRegistry(lExtended)
    ENDIF
    VAR o   := Environment.OSVersion
    VAR cOs := o:ToString()
	IF !lExtended
		SWITCH o:Platform
		CASE PlatformID.Win32NT
			cOs := "Windows NT"
		CASE PlatformID.Win32S
			cOs := "Win32s on Windows 3.1"
		CASE PlatformID.Win32Windows
			IF o:Version:Major == 4
				IF o:Version:Minor == 0
					cOs := "Windows 95"
				ELSEIF o:Version:Minor == 10
					cOs := "Windows 98"
					IF o:Version:Build == 2222
						cOs := "Windows 98 SE"
					ENDIF
				ELSEIF o:Version:Minor == 90
					cOs := "Windows ME"
				ENDIF
			ENDIF
		CASE PlatformID.WinCE
			cOs := "Windows CE"
		OTHERWISE
			cOs := o:ToString()
		END SWITCH
	ENDIF
    RETURN cOs

FUNCTION ReadOsVersionFromRegistry(lExtended AS LOGIC) AS STRING

    VAR registryKey := Microsoft.Win32.Registry.LocalMachine:OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion")
	VAR cOS := (STRING)registryKey:GetValue("productName")
    IF lExtended
        VAR os := Environment.OSVersion
	    VAR pa := Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
	    VAR isx86 := pa:ToLower():Contains("x86")
	    cOS += " (" + IIF(isx86, "x86","x64") + i") ( Version {os.Version.Major}.{os.Version.Minor}, Build {os.Version.Build} )"
    ENDIF
    RETURN cOS

FUNCTION IsRunningOnWindows() AS LOGIC
    SWITCH System.Environment.OSVersion:Platform
    CASE System.PlatformID.Win32NT
    CASE System.PlatformID.Win32S               // No longer in use
    CASE System.PlatformID.Win32Windows         // No longer in use
    CASE System.PlatformID.WinCE                // No longer in use
        RETURN TRUE
    CASE System.PlatformID.Xbox
    CASE System.PlatformID.Unix
        RETURN FALSE
    END SWITCH
    RETURN FALSE

FUNCTION TruePath( cFile AS STRING ) AS STRING PASCAL
    LOCAL           nPos        AS DWORD
    LOCAL           cPath       AS STRING

    nPos  := RAt("\", cFile)
    IF nPos = 0
        cPath := cFile
    ELSE
        cPath := cFile:Substring(0, (INT) nPos)
    ENDIF

    RETURN cPath

/// <exclude/>
FUNCTION _ExecName() AS STRING
    RETURN System.Reflection.Assembly.GetEntryAssembly():Location

FUNCTION ExecName( lFull AS LOGIC ) AS STRING
    LOCAL   nPos        AS DWORD
    LOCAL   cPath       AS STRING

    cPath := _ExecName()

    IF ! lFull
        nPos  := RAt( "\", cPath )

        IF nPos != 0
            cPath := SubStr2( cPath, nPos + 1 )
        ENDIF
    ENDIF

    RETURN cPath

FUNCTION ModuleName( lFull AS LOGIC ) AS STRING
    RETURN ExecName( lFull )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getcmdline/*" />
FUNCTION _GetCmdLine() AS STRING
RETURN System.Environment.CommandLine

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getinst/*" />
FUNCTION _GetInst() AS IntPtr
    LOCAL oType AS System.Type
    oType := Typeof(Functions)
    RETURN System.Runtime.InteropServices.Marshal.GetHINSTANCE(oType:Module)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getcmdshow/*" />
[Obsolete];
FUNCTION _GetCmdShow() AS LONG
    RETURN 1

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getprevinst/*" />
[Obsolete];
FUNCTION _GetPrevInst AS IntPtr
    RETURN IntPtr.Zero

/// <summary>Terminate application processing, close all open files, and return control to the operating system.</summary>
/// <remarks>This function can be used from anywhere in an application.
/// A RETURN executed from the Start() function can also be used to QUIT an application.</remarks>
FUNCTION _Quit() AS VOID
    System.Diagnostics.Process.GetCurrentProcess():CloseMainWindow()
    RETURN

//
// 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 XSharp
USING System.IO

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/doserror/*" />
FUNCTION DosError(nNewDosCode AS DWORD) AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	System.Environment.ExitCode := UNCHECKED((INT) nNewDosCode)
	RETURN UNCHECKED((DWORD) nOld)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/doserror/*" />
FUNCTION DosError() AS DWORD
	LOCAL nOld AS INT
	nOld := System.Environment.ExitCode
	RETURN UNCHECKED((DWORD) nOld)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getdoserror/*" />
FUNCTION GetDosError() AS DWORD
	RETURN UNCHECKED((DWORD) System.Runtime.InteropServices.Marshal.GetLastWin32Error())

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/getenv/*" />
FUNCTION GetEnv(cEnvVariable AS STRING) AS STRING
	RETURN System.Environment.GetEnvironmentVariable(cEnvVariable)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/setenv/*" />

FUNCTION SetEnv(cVar AS STRING,cValue AS STRING) AS LOGIC
	RETURN SetEnv(cVar, cValue, FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/setenv/*" />
FUNCTION SetEnv(cVar AS STRING,cValue AS STRING,lAppend AS LOGIC) AS LOGIC
	LOCAL result AS LOGIC
	TRY
        XSharp.IO.File.ClearErrorState()

		IF lAppend
			LOCAL cOldValue AS STRING
			cOldValue := System.Environment.GetEnvironmentVariable(cVar)
			IF ! String.IsNullOrEmpty( cOldValue )
				cOldValue += ";"
			ENDIF
			cValue := cOldValue + cValue
		ENDIF
		System.Environment.SetEnvironmentVariable(cVar, cValue)
		result := System.Environment.GetEnvironmentVariable(cVar) == cValue
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
		result := FALSE
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/netname/*" />

FUNCTION NetName() AS STRING
	RETURN System.Environment.MachineName

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdir/*" />
FUNCTION CurDir (cDrive AS STRING) AS STRING
	RETURN CurDir()

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdir/*" />
FUNCTION CurDir() AS STRING
	LOCAL cDir AS STRING
	LOCAL index AS INT
	cDir := System.Environment.CurrentDirectory
	index := cDir:IndexOf(Path.VolumeSeparatorChar)
	IF index > 0
		cDir := cDir:Substring(index+1)
	ENDIF
	IF cDir:Length > 0 .AND. cDir[0] == Path.DirectorySeparatorChar
		cDir := cDir:Substring(1)
	ENDIF
	IF cDir:Length > 0 .AND. cDir[cDir:Length-1]  == Path.DirectorySeparatorChar
		cDir := cDir:Substring(0, cDir:Length-1)
	ENDIF
	RETURN cDir

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/curdrive/*" />
FUNCTION CurDrive() AS STRING
	LOCAL currentDirectory := System.IO.Directory.GetCurrentDirectory() AS STRING
	LOCAL drive := "" AS STRING
	LOCAL position AS INT

	position := currentDirectory:IndexOf(System.IO.Path.VolumeSeparatorChar)
	IF position > 0
		drive := currentDirectory:Substring(0,position)
	ENDIF
	RETURN drive

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/workdir/*" />
FUNCTION WorkDir() AS STRING
	LOCAL cPath AS STRING
	LOCAL asm   AS System.Reflection.Assembly
	asm := System.Reflection.Assembly.GetCallingAssembly()
	cPath := asm:ManifestModule:FullyQualifiedName
	cPath := Path.GetDirectoryName(cPath)
	IF cPath[cPath:Length-1] !=  Path.DirectorySeparatorChar
		cPath += Path.DirectorySeparatorChar:ToString()
	ENDIF
	RETURN cPath

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskfree/*" />
FUNCTION DiskFree() AS INT64
	RETURN DiskFree(CurDrive())

INTERNAL FUNCTION DiskNo2DiskName(nDisk AS INT) AS STRING
    nDisk := _AND(nDisk, 0xFF)
    IF nDisk >= 0 .AND. nDisk <= 25
        LOCAL charDrive AS CHAR
        charDrive := (CHAR) ( 65 + (nDisk-1))
	    RETURN charDrive:ToString()
    ELSE
        RETURN CurDrive()
    ENDIF

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskfree/*" />
FUNCTION DiskFree(cDrive AS STRING) AS INT64
    LOCAL result AS INT64
    IF String.IsNullOrEmpty(cDrive)
        cDrive := CurDrive()
    ELSEIF cDrive:Length > 1
        cDrive := cDrive:Substring(0,1)+":"
    ENDIF
    TRY
        XSharp.IO.File.ClearErrorState()
	    result :=  DriveInfo{cDrive}:TotalFreeSpace
    CATCH e AS Exception
        XSharp.IO.File.SetErrorState(e)
        result := 0
    END TRY
    RETURN result

/// <inheritdoc cref="DiskFree" />
/// <param name="nDrive">The number of the disk drive to query, where 1 is drive A, 2 is B, 3 is C, and so on. </param>
FUNCTION DiskFree(nDrive AS INT) AS INT64
	RETURN DiskFree(DiskNo2DiskName(nDrive))

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskname/*" />
FUNCTION DiskName() AS STRING
	RETURN CurDrive()

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskspace/*" />
FUNCTION DiskSpace() AS INT64
	RETURN DiskSpace(CurDrive())

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskspace/*" />
FUNCTION DiskSpace(nDrive AS INT) AS INT64
	LOCAL cDisk AS STRING
	cDisk := DiskNo2DiskName(nDrive)
	RETURN DiskSpace(cDisk)

/// <inheritdoc cref="DiskSpace" />
/// <param name="cDrive">The name of the drive as a string, for example "C:", "A:".  If you do not specify a drive, the Windows default is used.</param>
FUNCTION DiskSpace(cDrive AS STRING) AS INT64
    LOCAL result AS INT64
    IF String.IsNullOrEmpty(cDrive)
        cDrive := CurDrive()
    ELSEIF cDrive:Length > 1
        cDrive := cDrive:Substring(0,1)+":"
    ENDIF
    TRY
        XSharp.IO.File.ClearErrorState()
	    result :=  DriveInfo{cDrive}:TotalSize
    CATCH e AS Exception
        XSharp.IO.File.SetErrorState(e)
        result := 0
    END TRY
    RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/neterr/*" />
FUNCTION NetErr() AS LOGIC STRICT
    RETURN RuntimeState.NetErr

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/neterr/*" />
FUNCTION NetErr( lNewError AS LOGIC ) AS LOGIC
    LOCAL curvalue := RuntimeState.NetErr AS LOGIC
    RuntimeState.NetErr := lNewError
    RETURN curvalue

/// <summary>
/// </summary>
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries() AS DWORD
	RETURN RuntimeState.LockTries

/// <summary>
/// </summary>
/// <param name="n"></param>
/// <returns>
/// </returns>
FUNCTION LockTries(nValue AS DWORD) AS DWORD
	LOCAL nResult AS DWORD
	nResult := RuntimeState.LockTries
	RuntimeState.LockTries := nValue
	RETURN nResult

INTERNAL FUNCTION BadFileParam(cFunction AS STRING, cParam AS STRING, nParam AS DWORD) AS VOID
    RuntimeState.FileError      := FERROR_PARAM
    RuntimeState.FileException  := Error.ArgumentError(cFunction,cParam, nParam,"Empty file or directoryname is not allowed")
    RuntimeState.NetErr         := FALSE
RETURN

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirchange/*" />
FUNCTION DirChange(pszDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszDir)
		   BadFileParam(__FUNCTION__, nameof(pszDir), 1)
		   RETURN -1
		ENDIF
		IF Directory.Exists(pszDir)
			Directory.SetCurrentDirectory(pszDir)
			result := 0
		ELSE
			result := 3 // Path not found
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirmake/*" />
FUNCTION DirMake(pszNewDir AS STRING) AS INT
	LOCAL result AS INT
	TRY
        XSharp.IO.File.ClearErrorState()
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszNewDir)
		   BadFileParam(__FUNCTION__, nameof(pszNewDir), 1)
		   RETURN -1
		ENDIF
		IF !Directory.Exists(pszNewDir)
			Directory.CreateDirectory(pszNewDir)
			result := 0
		ELSE
			result := 183 // ERROR_ALREADY_EXISTS
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/dirremove/*" />
FUNCTION DirRemove(pszDirName AS STRING) AS INT
	LOCAL result AS INT
	TRY
		XSharp.IO.File.ClearErrorState()
		IF String.IsNullOrEmpty(pszDirName)
		   BadFileParam(__FUNCTION__, nameof(pszDirName), 1)
		   RETURN -1
		ENDIF
		IF Directory.Exists(pszDirName)
			Directory.Delete(pszDirName,FALSE)
			result := 0
		ELSE
			result := 2 // Cannot find file
		ENDIF
	CATCH e AS Exception
		XSharp.IO.File.SetErrorState(e)
        result := (INT) RuntimeState.FileError
	END TRY
	RETURN result

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/diskchange/*" />
FUNCTION DiskChange(pszDrive AS STRING) AS LOGIC
	IF String.IsNullOrEmpty(pszDrive)
		RETURN FALSE
	ENDIF
	pszDrive := pszDrive:Substring(0,1)+Path.VolumeSeparatorChar:ToString()+Path.DirectorySeparatorChar:ToString()
	RETURN DirChange(pszDrive) == 0

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/os/*" />
FUNCTION OS() AS STRING
	RETURN OS(FALSE)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/os/*" />
FUNCTION OS(lExtended AS LOGIC) AS STRING
    IF IsRunningOnWindows()
        RETURN ReadOsVersionFromRegistry(lExtended)
    ENDIF
    VAR o   := Environment.OSVersion
    VAR cOs := o:ToString()
	IF !lExtended
		SWITCH o:Platform
		CASE PlatformID.Win32NT
			cOs := "Windows NT"
		CASE PlatformID.Win32S
			cOs := "Win32s on Windows 3.1"
		CASE PlatformID.Win32Windows
			IF o:Version:Major == 4
				IF o:Version:Minor == 0
					cOs := "Windows 95"
				ELSEIF o:Version:Minor == 10
					cOs := "Windows 98"
					IF o:Version:Build == 2222
						cOs := "Windows 98 SE"
					ENDIF
				ELSEIF o:Version:Minor == 90
					cOs := "Windows ME"
				ENDIF
			ENDIF
		CASE PlatformID.WinCE
			cOs := "Windows CE"
		OTHERWISE
			cOs := o:ToString()
		END SWITCH
	ENDIF
    RETURN cOs

FUNCTION ReadOsVersionFromRegistry(lExtended AS LOGIC) AS STRING

    VAR registryKey := Microsoft.Win32.Registry.LocalMachine:OpenSubKey("Software\\Microsoft\\Windows NT\\CurrentVersion")
	VAR cOS := (STRING)registryKey:GetValue("productName")
    IF lExtended
        VAR os := Environment.OSVersion
	    VAR pa := Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE")
	    VAR isx86 := pa:ToLower():Contains("x86")
	    cOS += " (" + IIF(isx86, "x86","x64") + i") ( Version {os.Version.Major}.{os.Version.Minor}, Build {os.Version.Build} )"
    ENDIF
    RETURN cOS

FUNCTION IsRunningOnWindows() AS LOGIC
    SWITCH System.Environment.OSVersion:Platform
    CASE System.PlatformID.Win32NT
    CASE System.PlatformID.Win32S               // No longer in use
    CASE System.PlatformID.Win32Windows         // No longer in use
    CASE System.PlatformID.WinCE                // No longer in use
        RETURN TRUE
    CASE System.PlatformID.Xbox
    CASE System.PlatformID.Unix
        RETURN FALSE
    END SWITCH
    RETURN FALSE

FUNCTION TruePath( cFile AS STRING ) AS STRING PASCAL
    LOCAL           nPos        AS DWORD
    LOCAL           cPath       AS STRING

    nPos  := RAt("\", cFile)
    IF nPos = 0
        cPath := cFile
    ELSE
        cPath := cFile:Substring(0, (INT) nPos)
    ENDIF

    RETURN cPath

/// <exclude/>
FUNCTION _ExecName() AS STRING
    RETURN System.Reflection.Assembly.GetEntryAssembly():Location

FUNCTION ExecName( lFull AS LOGIC ) AS STRING
    LOCAL   nPos        AS DWORD
    LOCAL   cPath       AS STRING

    cPath := _ExecName()

    IF ! lFull
        nPos  := RAt( "\", cPath )

        IF nPos != 0
            cPath := SubStr2( cPath, nPos + 1 )
        ENDIF
    ENDIF

    RETURN cPath

FUNCTION ModuleName( lFull AS LOGIC ) AS STRING
    RETURN ExecName( lFull )

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getcmdline/*" />
FUNCTION _GetCmdLine() AS STRING
RETURN System.Environment.CommandLine

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getinst/*" />
FUNCTION _GetInst() AS IntPtr
    LOCAL oType AS System.Type
    oType := Typeof(Functions)
    RETURN System.Runtime.InteropServices.Marshal.GetHINSTANCE(oType:Module)

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getcmdshow/*" />
[Obsolete];
FUNCTION _GetCmdShow() AS LONG
    RETURN 1

/// <include file="VoFunctionDocs.xml" path="Runtimefunctions/_getprevinst/*" />
[Obsolete];
FUNCTION _GetPrevInst AS IntPtr
    RETURN IntPtr.Zero

/// <summary>Terminate application processing, close all open files, and return control to the operating system.</summary>
/// <remarks>This function can be used from anywhere in an application.
/// A RETURN executed from the Start() function can also be used to QUIT an application.</remarks>
FUNCTION _Quit() AS VOID
    System.Diagnostics.Process.GetCurrentProcess():CloseMainWindow()
    RETURN