;-- TIME_STAMP   2019-11-20 20:58:33   v 0.2

#include-once
#include <File.au3>

Opt('MustDeclareVars', 1)

#cs
Struktur Config-Datei (*.cfg)

== Sektion mit Inhalt ==
SECTION;Key=Value;Key=;Key=Value;Key=Value...

Schlüssel ohne Wert MÜSSEN mit einem "=" abgeschlossen werden!

== leere Sektion ==
SECTION

Kommentarzeilen beginnen mit "#"

Funktionen:

_CfgRead()
_CfgWrite()
_CfgKeyDelete()
_CfgSectionDelete()
_CfgKeyList()
_CfgSectionList()
_CfgSectionGetArray()

#ce


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgRead
; Description ...: Reads the value of a key in a section.
; Syntax ........: _CfgRead($_sCfg, $_sSection, $_sKey[, $_sDefault = ''])
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - Section with the searched key
;                  $_sKey               - The searched key
;                  $_sDefault           - [optional] The default return value. Default is ''.
; Return values .: Success              The value of the passed key or if not set, the default value
;                  Failure              The default value, set @error  1 - config file does'nt exist
;                                       The default value, set @error  2 - config file is empty
;                                       The default value, set @error  3 - none key-value-pairs in this section
;                                       The default value, set @error  4 - key does'nt exist in this section
;                                       The default value, set @error  5 - section does'nt exist
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgRead($_sCfg, $_sSection, $_sKey, $_sDefault='')
	If Not FileExists($_sCfg) Then Return SetError(1,0,$_sDefault)
	Local $aSectionLine, $aKeyVal, $iErr
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,$_sDefault)            ; empty file
	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$iErr = @error
		If $aSectionLine[0] = $_sSection Then
			If $iErr = 1 Then Return SetError(3,0,$_sDefault)     ; none key-value-pairs in this line
			For $j = 1 To UBound($aSectionLine) -1
				$aKeyVal = StringSplit($aSectionLine[$j], '=', 2) ; $STR_NOCOUNT (2)
				If $aKeyVal[0] = $_sKey Then
					If UBound($aKeyVal) = 2 Then
						Return $aKeyVal[1]
					Else
						Return $_sDefault
					EndIf
				EndIf
			Next
			Return SetError(4,0,$_sDefault)
		EndIf
	Next
	Return SetError(5,0,$_sDefault)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgWrite
; Description ...: Writes the value of a key in a section.
; Syntax ........: _CfgWrite($_sCfg, $_sSection, $_sKey, $_sValue)
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - The section
;                  $_sKey               - The key
;                  $_sValue             - The value to store
; Return values .: Success              1
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgWrite($_sCfg, $_sSection, $_sKey, $_sValue)
	If Not FileExists($_sCfg) Then
		Return FileWrite($_sCfg, StringFormat('%s;%s=%s\n', $_sSection, $_sKey, $_sValue))
	EndIf
	Local $aSectionLine, $aKeyVal, $fH, $sLine, $iErr, $bReplaced
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then                                            ; empty file
		$fH = FileOpen($_sCfg, 2)                                 ; $FO_OVERWRITE (2)
		FileWrite($fH, StringFormat('%s;%s=%s\n', $_sSection, $_sKey, $_sValue))
		Return FileClose($fH)
	EndIf

	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$iErr = @error
		If $aSectionLine[0] = $_sSection Then
			If $iErr = 1 Then                                     ; none key-value-pairs in this line
				Return _FileWriteToLine($_sCfg, $i+1, StringFormat('%s;%s=%s', $_sSection, $_sKey, $_sValue), True)
			EndIf
			$sLine = $aSectionLine[0]
			$bReplaced = False
			For $j = 1 To UBound($aSectionLine) -1
				$aKeyVal = StringSplit($aSectionLine[$j], '=', 2) ; $STR_NOCOUNT (2)
				If $aKeyVal[0] = $_sKey Then
					$sLine &= StringFormat(';%s=%s', $aKeyVal[0], $_sValue)
					$bReplaced = True
				Else
					$sLine &= StringFormat(';%s=%s', $aKeyVal[0], $aKeyVal[1])
				EndIf
			Next
			If Not $bReplaced Then $sLine &= StringFormat(';%s=%s', $_sKey, $_sValue)
			Return _FileWriteToLine($_sCfg, $i+1, $sLine, True)
		EndIf
	Next
	; section not found
	$fH = FileOpen($_sCfg, 1)                                     ; $FO_APPEND (1)
	FileWrite($fH, StringFormat('%s;%s=%s\n', $_sSection, $_sKey, $_sValue))
	Return FileClose($fH)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgKeyDelete
; Description ...: Deletes a key-value-pair in a section
; Syntax ........: _CfgKeyDelete($_sCfg, $_sSection, $_sKey)
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - Section with the searched key
;                  $_sKey               - The key to delete
; Return values .: Success              1
;                  Failure              0, set @error  1 - config file does'nt exist
;                                       0, set @error  2 - config file is empty
;                                       0, set @error  3 - none key-value-pairs in this section
;                                       0, set @error  4 - section does'nt exist
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgKeyDelete($_sCfg, $_sSection, $_sKey)
	If Not FileExists($_sCfg) Then Return SetError(1,0,0)
	Local $aSectionLine, $aKeyVal, $fH, $sLine, $iErr
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,0)                     ; empty file

	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$iErr = @error
		If $aSectionLine[0] = $_sSection Then
			If $iErr = 1 Then Return SetError(3,0,0)              ; none key-value-pairs in this line
			$sLine = $aSectionLine[0]
			For $j = 1 To UBound($aSectionLine) -1
				$aKeyVal = StringSplit($aSectionLine[$j], '=', 2) ; $STR_NOCOUNT (2)
				If $aKeyVal[0] = $_sKey Then
					; do nothing, will not write again
				Else
					$sLine &= StringFormat(';%s=%s', $aKeyVal[0], $aKeyVal[1])
				EndIf
			Next
			Return _FileWriteToLine($_sCfg, $i+1, $sLine, True)
		EndIf
	Next
	; section not found
	Return SetError(4,0,0)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgSectionDelete
; Description ...: Deletes a section
; Syntax ........: _CfgSectionDelete($_sCfg, $_sSection)
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - The Section to delete
; Return values .: Success              1
;                  Failure              0, set @error  1 - config file does'nt exist
;                                       0, set @error  2 - config file is empty
;                                       0, set @error  3 - section does'nt exist
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgSectionDelete($_sCfg, $_sSection)
	If Not FileExists($_sCfg) Then Return SetError(1,0,0)
	Local $aSectionLine
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,0)                     ; empty file
	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		If $aSectionLine[0] = $_sSection Then
			Return _FileWriteToLine($_sCfg, $i+1, '', True)
		EndIf
	Next
	; section not found
	Return SetError(3,0,0)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgSectionList
; Description ...: Returns a  array or string of all section names
; Syntax ........: _CfgSectionList($_sCfg[, $_bRetArray=True])
; Parameters ....: $_sCfg               - Path of the config file
;                  $_bRetArray          - [optional] Return as array or delimited string, Default is True - Array
; Return values .: Success              Array with all section names, count of sections on index [0]
;                  Failure              0, set @error  1 - config file does'nt exist
;                                       0, set @error  2 - config file is empty
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgSectionList($_sCfg, $_bRetArray=True)
	If Not FileExists($_sCfg) Then Return SetError(1,0,0)
	Local $aSectionLine, $aKeyVal, $sRet = ''
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,0)                     ; empty file

	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$sRet &= $aSectionLine[0] & ';'
	Next
	$sRet = StringTrimRight($sRet, 1)
	Return ($_bRetArray ? StringSplit($sRet, ';') : $sRet)
EndFunc



; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgKeyList
; Description ...: Returns a  array or string of all key names in a section
; Syntax ........: _CfgKeyList($_sCfg, $_sSection[, $_bRetArray=True])
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - Section to get the keys from
;                  $_bRetArray          - [optional] Return as array or delimited string, Default is True - Array
; Return values .: Success              1
;                  Failure              0, set @error  1 - config file does'nt exist
;                                       0, set @error  2 - config file is empty
;                                       0, set @error  3 - none key-value-pairs in this section
;                                       0, set @error  4 - section does'nt exist
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgKeyList($_sCfg, $_sSection, $_bRetArray=True)
	If Not FileExists($_sCfg) Then Return SetError(1,0,0)
	Local $aSectionLine, $aKeyVal, $iErr, $sRet = ''
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,0)                     ; empty file

	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$iErr = @error
		If $aSectionLine[0] = $_sSection Then
			If $iErr = 1 Then Return SetError(3,0,0)              ; none key-value-pairs in this line
			For $j = 1 To UBound($aSectionLine) -1
				$aKeyVal = StringSplit($aSectionLine[$j], '=', 2) ; $STR_NOCOUNT (2)
				$sRet &= $aKeyVal[0] & ';'
			Next
			$sRet = StringTrimRight($sRet, 1)
			Return ($_bRetArray ? StringSplit($sRet, ';') : $sRet)
		EndIf
	Next
	; section not found
	Return SetError(4,0,0)
EndFunc


; #FUNCTION# ====================================================================================================================
; Name ..........: _CfgSectionGetArray
; Description ...: Returns a  2D array of all key-value-pairs in a section
; Syntax ........: _CfgSectionGetArray($_sCfg, $_sSection)
; Parameters ....: $_sCfg               - Path of the config file
;                  $_sSection           - Section to get the key-value-pairs from
; Return values .: Success              2D array: [[count_keys,section_name],[key_1,value],[key_2,value], .. [key_n,value]]
;                  Failure              [[0,"error"]], set @error  1 - config file does'nt exist
;                                       [[0,"error"]], set @error  2 - config file is empty
;                                       [[0,"error"]], set @error  3 - section does'nt exist
; Author ........: BugFix
; ===============================================================================================================================
Func _CfgSectionGetArray($_sCfg, $_sSection)
	Local $aSectionLine, $aKeyVal, $iErr, $iCnt, $aRet[1][2] = [[0,"error"]]
	If Not FileExists($_sCfg) Then Return SetError(1,0,$aRet)
	Local $aCfg = FileReadToArray($_sCfg)
	If @error = 2 Then Return SetError(2,0,$aRet)                 ; empty file

	For $i = 0 To UBound($aCfg) -1
		If StringLeft($aCfg[$i], 1) = '#' Then ContinueLoop       ; its a comment line - skip
		$aSectionLine = StringSplit($aCfg[$i], ';', 2)            ; $STR_NOCOUNT (2)
		$iErr = @error
		$iCnt = UBound($aSectionLine)
		If $aSectionLine[0] = $_sSection Then
			If $iErr = 1 Then
				$aRet[0][1] = ''                                  ; none key-value-pairs in this line
				Return $aRet
			EndIf
			ReDim $aRet[$iCnt][2]
			$aRet[0][0] = $iCnt -1
			$aRet[0][1] = $_sSection
			For $j = 1 To UBound($aSectionLine) -1
				$aKeyVal = StringSplit($aSectionLine[$j], '=', 2) ; $STR_NOCOUNT (2)
				$aRet[$j][0] = $aKeyVal[0]
				$aRet[$j][1] = (UBound($aKeyVal) = 2 ? $aKeyVal[1] : '')
			Next
			Return $aRet
		EndIf
	Next
	; section not found
	Return SetError(3,0,$aRet)
EndFunc
