#include-once
#include <File.au3>
#include <String.au3>

#cs ===== Funktionsliste =======================================================

	Erstellen / Verwalten von Schlüssel-Wert-Paaren ohne INI-Größenlimit

	Musterdatei:
		<sec>Sektion 1
		<key>Schlüssel 1_1<val>Wert 1_1</val></key>
		<key>Schlüssel 1_2<val>Wert 1_2</val></key>
		<key>Schlüssel 1_3<val>Wert 1_3</val></key>
		<key>Schlüssel 1_4<val>Wert 1_4</val></key>
		</sec>

	Funktionen sind identisch zu den bekannten Standard-Ini-Funktionen

	_IniEx_ReadSectionNames
	_IniEx_ReadSection
	_IniEx_Read
	_IniEx_RenameSection
	_IniEx_Write
	_IniEx_WriteSection

#ce ============================================================================


;===============================================================================
; Function Name..:   _IniEx_ReadSectionNames
; Description....:   Liest die Sektionsnamen der IniEx-Datei aus
; Parameter(s)...:   $sPath     Pfad zur IniEx-Datei
; Return Value(s):   Erfolg     Array mit den Sektionsnamen, Anzahl in Array[0]
;                    Fehler   0  @error 1 - Datei nicht gefunden
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_ReadSectionNames($sPath)
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $sRead = FileRead($sPath)
	Local $aSec = _StringBetween($sRead, '<sec>', @CRLF)
	Local $aOut[UBound($aSec) + 1]
	$aOut[0] = UBound($aSec)
	For $i = 0 To UBound($aSec) - 1
		$aOut[$i + 1] = $aSec[$i]
	Next
	Return $aOut
EndFunc   ;==>_IniEx_ReadSectionNames

;===============================================================================
; Function Name..:   _IniEx_ReadSection
; Description....:   Liest die angegebene Sektion aus
; Parameter(s)...:   $sPath     Pfad zur IniEx-Datei
;                    $sSection  Sektionsname
; Return Value(s):   Erfolg     2D-Array, Array[0][0] enthält Anzahl, [n][0]=Key, [n][1]=Value
;                    Fehler     0  @error 1 - Datei nicht gefunden
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_ReadSection($sPath, $sSection)
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $fInSection = False, $aIni, $iKeys = 0, $aPairs[1][2] = [[$iKeys]], $aKeyVal, $sKeyVal
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0]
		If Not $fInSection And $aIni[$i] <> '<sec>' & $sSection Then ContinueLoop
		$fInSection = True
		If $aIni[$i] = '</sec>' Then ExitLoop
		If StringLeft($aIni[$i], 5) = '<key>' Then
			$aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
			If @error Then ContinueLoop
			$aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
			$iKeys += 1
			ReDim $aPairs[$iKeys + 1][2]
			$aPairs[0][0] = $iKeys
			$aPairs[$iKeys][0] = $aKeyVal[0]
			$aPairs[$iKeys][1] = $aKeyVal[1]
		EndIf
	Next
	Return $aPairs
EndFunc   ;==>_IniEx_ReadSection

;===============================================================================
; Function Name..:   _IniEx_Read
; Description....:   Liest den Wert für den Schlüssel einer Sektion aus
; Parameter(s)...:   $sPath     Pfad zur IniEx-Datei
;                    $sSection  Sektionsname
;                    $sKey      Schlüsselname
;                    $sDefault  Defaultwert, wenn Key nicht gefunden
; Return Value(s):   Erfolg     Wert des Schlüssels
;                    Fehler     0  @error 1 - Datei nicht gefunden
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_Read($sPath, $sSection, $sKey, $sDefault = 'Default')
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $fInSection = False, $aIni, $iKeys = 0, $aPairs[1][2] = [[$iKeys]], $aKeyVal, $sKeyVal
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0]
		If Not $fInSection And $aIni[$i] <> '<sec>' & $sSection Then ContinueLoop
		$fInSection = True
		If $aIni[$i] = '</sec>' Then ExitLoop
		If StringLeft($aIni[$i], 5) = '<key>' Then
			$aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
			If @error Then ContinueLoop
			$aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
			If $aKeyVal[0] = $sKey Then Return $aKeyVal[1]
		EndIf
	Next
	Return $sDefault
EndFunc   ;==>_IniEx_Read

;===============================================================================
; Function Name..:   _IniEx_RenameSection
; Description....:   Umbenennen einer Sektion
; Parameter(s)...:   $sPath        Pfad zur IniEx-Datei
;                    $sSection     Sektionsname
;                    $sNewSection  neuer Sektionsname
;                    $fOverwrite   falls sNewSection bereits besteht wird Inhalt gelöscht, Standard=False
; Return Value(s):   Erfolg        1
;                    Fehler        0   @error 1 - Datei nicht gefunden
;                                             2 - Sektionsname existiert nicht
;                                             3 - Sektion existiert aber Überschreib-Flag nicht gesetzt
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_RenameSection($sPath, $sSection, $sNewSection, $fOverwrite = False)
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $fExists = False, $fInSection = False, $aIni, $iFirstLine = 0, $iLastLine
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0] ; check ob $sNewSection bereits existiert
		If $aIni[$i] = '<sec>' & $sNewSection Then
			$fExists = True
			ExitLoop
		EndIf
	Next
	For $i = 1 To $aIni[0]
		If Not $fInSection And $aIni[$i] = '<sec>' & $sSection Then ; Sektion Startzeile suchen
			$fInSection = True
			$iFirstLine = $i
			ContinueLoop
		EndIf
		If $fInSection And Not $fExists Then ExitLoop ; $sNewSection existiert noch nicht >> Endzeile braucht nicht ermittelt werden
		If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
			$iLastLine = $i
			$fInSection = False
			ExitLoop
		EndIf
	Next
	If $iFirstLine = 0 Then Return SetError(2, 0, 0) ; Sektionsname nicht enthalten
	If Not $fExists Then Return _FileWriteToLine($sPath, $iFirstLine, '<sec>' & $sNewSection, 1)
	If $fExists And Not $fOverwrite Then
		Return SetError(3, 0, 0) ; $sNewSection existiert aber Überschreib-Flag nicht gesetzt
	Else ; alte Sektion kpl. löschen
		For $i = $iLastLine To $iFirstLine Step -1
			_FileWriteToLine($sPath, $iFirstLine, '', 1)
		Next
	EndIf
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0]
		If Not $fInSection And $aIni[$i] = '<sec>' & $sNewSection Then ; Sektion Startzeile suchen
			$fInSection = True
			$iFirstLine = $i
			ContinueLoop
		EndIf
		If Not $fExists Then ExitLoop ; $sNewSection existiert noch nicht >> Endzeile braucht nicht ermittelt werden
		If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
			$iLastLine = $i
			$fInSection = False
			ExitLoop
		EndIf
	Next
	For $i = $iLastLine - 1 To $iFirstLine + 1 Step -1 ; Sektion mit selbem Namen, wie $sNewSection leeren
		_FileWriteToLine($sPath, $i, '', 1)
	Next
	Return 1
EndFunc   ;==>_IniEx_RenameSection

;===============================================================================
; Function Name..:   _IniEx_Write
; Description....:   Schreibt einen neuen Wert in einen Schlüssel oder fügt ein neues Schlüssel-Wert-Paar ein
; Parameter(s)...:   $sPath        Pfad zur IniEx-Datei
;                    $sSection     Sektionsname
;                    $sKey         Schlüsselname
;                    $sValue       Wert
; Return Value(s):   Erfolg        Letzter Wert des Schlüssels, bei Neuanlage Leerstring
;                    Fehler        0   @error 1 - Datei nicht gefunden
;                                             2 - Sektion existiert nicht
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_Write($sPath, $sSection, $sKey, $vValue)
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $fInSection = False, $aIni, $aKeyVal, $vOldVal = '', $fKeyNew = False, $iKeyLine = 0
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0]
		If Not $fInSection And $aIni[$i] = '<sec>' & $sSection Then ; Sektion Startzeile suchen
			$fInSection = True
			ContinueLoop
		EndIf
		If $fInSection And StringLeft($aIni[$i], 5) = '<key>' Then
			$aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
			$aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
			If $aKeyVal[0] = $sKey Then
				$iKeyLine = $i
				$vOldVal = $aKeyVal[1]
				ExitLoop
			EndIf
		EndIf
		If $fInSection And $aIni[$i] = '</sec>' Then ; Sektionsende
			If $iKeyLine = 0 Then
				$fKeyNew = True
				$iKeyLine = $i
			EndIf
			ExitLoop
		EndIf
	Next
	If Not $fInSection Then Return SetError(2, 0, 0) ; Sektion existiert nicht
	If $fKeyNew Then
		_FileWriteToLine($sPath, $iKeyLine, '<key>' & $sKey & '<val>' & $vValue & '</val></key>')
	Else
		_FileWriteToLine($sPath, $iKeyLine, '<key>' & $sKey & '<val>' & $vValue & '</val></key>', 1)
	EndIf
	Return $vOldVal ; Rückgabe alter Wert
EndFunc   ;==>_IniEx_Write

;===============================================================================
; Function Name..:   _IniEx_WriteSection
; Description....:   Schreibt eine Sektion in die IniEx-Datei
; Parameter(s)...:   $sPath        Pfad zur IniEx-Datei
;                    $sSection     Sektionsname
;                    $vData        String (Schlüssel=Wert, mehrere mit @LF trennen) oder Array [n][0]= Schlüssel, [n][1]= Wert
;                    $iIndex       Falls Array übergeben, der Startindex (Default=1)
; Return Value(s):   Erfolg        1
;                    Fehler        0  @error 1 - Datei nicht gefunden
;                                            2 - kein 2D-Array übergeben
; Author(s)......:   BugFix (bugfix@autoit.de)
;===============================================================================
Func _IniEx_WriteSection($sPath, $sSection, $vData, $iIndex = 1)
	If Not FileExists($sPath) Then Return SetError(1, 0, 0)
	Local $aIni, $aTmp, $aSplit, $aData[1][2] = [[0]], $sSectionData
	Local $fInSection = False, $iFirstLine = 0, $iLastLine, $sRead, $fh
	If Not IsArray($vData) Then ; Daten in Array packen
		$aTmp = StringSplit($vData, @LF, 1)
		ReDim $aData[$aTmp[0] + 1][2]
		$aData[0][0] = $aTmp[0]
		For $i = 1 To $aTmp[0]
			$aSplit = StringSplit($aTmp[$i], '=')
			$aData[$i][0] = $aSplit[1]
			$aData[$i][1] = $aSplit[2]
		Next
	Else
		If UBound($aData, 2) <> 2 Then Return SetError(2, 0, 0)
		$aData = $vData
	EndIf
	_FileReadToArray($sPath, $aIni)
	For $i = 1 To $aIni[0] ; check ob $sSection bereits existiert
		If $aIni[$i] = '<sec>' & $sSection Then
			$iFirstLine = $i
			$fInSection = True
		EndIf
		If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
			$iLastLine = $i
			$fInSection = False
		EndIf
	Next
	$sSectionData = '<sec>' & $sSection & @CRLF ; zu schreibenden Sektions-String zusammenstellen
	For $i = $iIndex To UBound($aData) - 1
		$sSectionData &= '<key>' & $aData[$i][0] & '<val>' & $aData[$i][1] & '</val></key>' & @CRLF
	Next
	$sSectionData = $sSectionData & '</sec>'
	If $iFirstLine > 0 Then ; Sektion besteht bereits
		For $i = $iLastLine To $iFirstLine + 1 Step -1
			_FileWriteToLine($sPath, $iFirstLine, '', 1)
		Next
		Return _FileWriteToLine($sPath, $iFirstLine, $sSectionData, 1) ; Sektion neu schreiben
	Else ; neue Sektion am Ende anfügen
		$fh = FileOpen($sPath, 1)
		FileWrite($fh, @CRLF & $sSectionData)
		Return FileClose($fh)
	EndIf
EndFunc   ;==>_IniEx_WriteSection