1
1
Fork 0
mirror of https://github.com/FellippeHeitor/InForm.git synced 2024-05-12 06:50:12 +00:00
InForm/InForm/extensions/Ini.bas
2024-01-11 04:51:14 +05:30

802 lines
29 KiB
QBasic

'-----------------------------------------------------------------------------------------------------------------------
' INI Manager
' Copyright (c) 2024 Samuel Gomes
' Copyright (c) 2022 Fellippe Heitor
'-----------------------------------------------------------------------------------------------------------------------
$IF INI_BAS = UNDEFINED THEN
$LET INI_BAS = TRUE
'$INCLUDE:'Ini.bi'
SUB IniSortSection (file$, __section$)
SHARED IniCODE, IniLastKey$, IniWholeFile$
SHARED IniDisableAutoCommit
REDIM Keys(1 TO 100) AS STRING
DIM TotalKeys, tempValue$, i AS LONG, Backup$, CommitBackup
IF IniFormatSection$(__section$) = "[]" THEN IniCODE = 15: EXIT SUB
DO
tempValue$ = ReadSetting(file$, __section$, "")
IF LEFT$(IniINFO$, 7) = "ERROR: " THEN EXIT SUB
IF IniCODE = 10 THEN EXIT DO
TotalKeys = TotalKeys + 1
IF TotalKeys > UBOUND(Keys) THEN
REDIM _PRESERVE Keys(1 TO UBOUND(Keys) + 100) AS STRING
END IF
Keys(TotalKeys) = IniLastKey$ + "=" + tempValue$
LOOP
REDIM _PRESERVE Keys(1 TO TotalKeys) AS STRING
IF IniArraySort(Keys()) = 0 THEN IniCODE = 23: EXIT SUB
CommitBackup = IniDisableAutoCommit
IniDisableAutoCommit = -1 'Prevent every minor change from being written to disk
Backup$ = IniWholeFile$
FOR i = 1 TO TotalKeys
IniDeleteKey file$, __section$, LEFT$(Keys(i), INSTR(Keys(i), "=") - 1)
IF LEFT$(IniINFO$, 7) = "ERROR: " THEN
IniDisableAutoCommit = CommitBackup
IniWholeFile$ = Backup$
EXIT SUB
END IF
NEXT
FOR i = 1 TO TotalKeys
WriteSetting file$, __section$, LEFT$(Keys(i), INSTR(Keys(i), "=") - 1), MID$(Keys(i), INSTR(Keys(i), "=") + 1)
IF LEFT$(IniINFO$, 7) = "ERROR: " THEN
IniDisableAutoCommit = CommitBackup
IniWholeFile$ = Backup$
EXIT SUB
END IF
NEXT
IniDisableAutoCommit = CommitBackup 'Restore writing to disk (or previously set state) and
IniCommit ' commit changes.
IniCODE = 22
END SUB
SUB IniDeleteSection (file$, __section$)
SHARED IniNewFile$, IniCODE, currentIniFileName$
SHARED IniLF$, IniWholeFile$, currentIniFileLOF AS _UNSIGNED LONG
IniLoad file$
IF IniCODE THEN EXIT SUB
DIM a$
IniCODE = 0
a$ = IniGetSection(__section$)
IF IniCODE THEN EXIT SUB
IniNewFile$ = LEFT$(IniWholeFile$, INSTR(IniWholeFile$, a$) - 1)
IniNewFile$ = IniNewFile$ + MID$(IniWholeFile$, INSTR(IniWholeFile$, a$) + LEN(a$ + IniLF$))
IniCommit
IniCODE = 13
END SUB
SUB IniDeleteKey (file$, __section$, __key$)
SHARED IniPosition AS _UNSIGNED LONG, IniCODE
SHARED IniLF$, IniWholeFile$, IniSectionData$
SHARED IniLastSection$, IniLastKey$, IniNewFile$
DIM tempValue$
DIM section$, key$
DIM FoundLF AS _UNSIGNED LONG
IniCODE = 0
'prepare variables for the write operation
section$ = IniFormatSection$(__section$)
IF IniCODE THEN EXIT SUB
key$ = LTRIM$(RTRIM$(__key$))
IF key$ = "" THEN IniCODE = 12: EXIT SUB
IniLastKey$ = key$
'Read the existing key to fill IniPosition
tempValue$ = ReadSetting$(file$, section$, key$)
IF IniCODE > 0 AND IniCODE <> 2 THEN EXIT SUB 'key not found
'map IniPosition (set in the section block) to the global file position
IniPosition = INSTR(IniWholeFile$, IniSectionData$) + IniPosition - 1
FoundLF = INSTR(IniPosition, IniWholeFile$, IniLF$)
IF FoundLF = 0 THEN FoundLF = LEN(IniWholeFile$)
IniNewFile$ = LEFT$(IniWholeFile$, IniPosition - 1) + MID$(IniWholeFile$, FoundLF + LEN(IniLF$))
IniCommit
IniCODE = 19
END SUB
SUB IniMoveKey (file$, __section$, __key$, __newsection$)
'A move operation is a copy operation + a delete operation
SHARED IniCODE
DIM tempValue$
tempValue$ = ReadSetting(file$, __section$, __key$)
IF IniCODE > 0 AND IniCODE <> 2 THEN EXIT SUB
WriteSetting file$, __newsection$, __key$, tempValue$
IF IniCODE > 0 AND IniCODE <> 2 AND IniCODE <> 7 AND IniCODE <> 9 THEN EXIT SUB
IniDeleteKey file$, __section$, __key$
IF IniCODE = 19 THEN IniCODE = 20
END SUB
SUB IniCommit
SHARED currentIniFileName$, IniWholeFile$, currentIniFileLOF AS _UNSIGNED LONG
SHARED IniNewFile$, IniDisableAutoCommit, IniCODE
SHARED LoadedFiles$
IF currentIniFileName$ = "" THEN IniCODE = 18: EXIT SUB
IniWholeFile$ = IniNewFile$
currentIniFileLOF = LEN(IniNewFile$)
IF NOT IniDisableAutoCommit THEN
DIM fileNum AS INTEGER, findFile AS INTEGER
'search LoadedFiles$, so we use the same file handle every time
findFile = INSTR(LoadedFiles$, "@" + currentIniFileName$ + "@")
IF findFile = 0 THEN
fileNum = FREEFILE
LoadedFiles$ = LoadedFiles$ + "@" + MKI$(fileNum) + "@" + currentIniFileName$ + "@"
ELSE
fileNum = CVI(MID$(LoadedFiles$, findFile - 2, 2))
CLOSE fileNum
END IF
OPEN currentIniFileName$ FOR BINARY AS #fileNum
IF LEN(IniWholeFile$) < LOF(fileNum) THEN
CLOSE fileNum
OPEN currentIniFileName$ FOR OUTPUT AS #fileNum: CLOSE #fileNum
OPEN currentIniFileName$ FOR BINARY AS #fileNum
END IF
PUT #fileNum, 1, IniNewFile$
CLOSE #fileNum 'flush
OPEN currentIniFileName$ FOR BINARY AS #fileNum 'keep open
END IF
END SUB
FUNCTION IniGetSection$ (__section$)
SHARED IniPosition AS _UNSIGNED LONG, IniCODE, currentIniFileName$
SHARED IniLF$, IniWholeFile$, currentIniFileLOF AS _UNSIGNED LONG
IF currentIniFileName$ = "" THEN IniCODE = 18: EXIT FUNCTION
IF currentIniFileLOF = 0 OR LEN(LTRIM$(RTRIM$(IniWholeFile$))) = 0 THEN IniCODE = 17: EXIT FUNCTION
IniCODE = 0
DIM section$, foundSection AS _UNSIGNED LONG, endSection AS _UNSIGNED LONG
DIM i AS _UNSIGNED LONG, Bracket1 AS _UNSIGNED LONG, sectionStart AS _UNSIGNED LONG
DIM inQuote AS _BYTE
section$ = IniFormatSection$(__section$)
IF IniCODE THEN EXIT FUNCTION
IF section$ = "[]" THEN
'fetch the "global" section, if present
sectionStart = INSTR(IniWholeFile$, "[")
IF sectionStart = 0 THEN IniGetSection$ = IniWholeFile$: EXIT FUNCTION
FOR i = sectionStart - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN foundSection = i + 1: EXIT FOR
IF ASC(IniWholeFile$, i) <> 32 THEN EXIT FOR
NEXT
IF i = 0 THEN foundSection = 1
IniGetSection$ = LEFT$(IniWholeFile$, foundSection - 1)
ELSE
DO
sectionStart = INSTR(sectionStart + 1, LCASE$(IniWholeFile$), LCASE$(section$))
IF sectionStart = 0 THEN IniCODE = 14: EXIT DO
'make sure it's a valid section header
foundSection = 0
FOR i = sectionStart - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN foundSection = i + 1: EXIT FOR
IF ASC(IniWholeFile$, i) <> 32 THEN EXIT FOR
NEXT
IF i = 0 THEN foundSection = 1
IF foundSection > 0 THEN
'we found it; time to identify where this section ends
'(either another [section] or the end of the file
Bracket1 = sectionStart
checkAgain:
Bracket1 = INSTR(Bracket1 + 1, IniWholeFile$, "[")
IF Bracket1 > 0 THEN
'found a bracket; check if it's inside quotes
inQuote = 0
FOR i = 1 TO Bracket1 - 1
IF ASC(IniWholeFile$, i) = 34 THEN inQuote = NOT inQuote
NEXT
IF inQuote THEN GOTO checkAgain
FOR i = Bracket1 - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN endSection = i + 1 - LEN(IniLF$): EXIT FOR
IF ASC(IniWholeFile$, i) = 61 THEN GOTO checkAgain 'bracket is inside a key's value
IF i <= foundSection THEN EXIT FOR
NEXT
IniGetSection$ = MID$(IniWholeFile$, foundSection, endSection - foundSection)
ELSE
IniGetSection$ = MID$(IniWholeFile$, foundSection)
END IF
EXIT FUNCTION
END IF
LOOP
END IF
END FUNCTION
FUNCTION IniFormatSection$ (__section$)
SHARED IniCODE
DIM section$
section$ = LTRIM$(RTRIM$(__section$))
'sections are in the format [section name] - add brackets if not passed
IF LEFT$(section$, 1) <> "[" THEN section$ = "[" + section$
IF RIGHT$(section$, 1) <> "]" THEN section$ = section$ + "]"
IF INSTR(MID$(section$, 2, LEN(section$) - 3), "[") OR INSTR(MID$(section$, 2, LEN(section$) - 3), "]") THEN
IniCODE = 15
EXIT FUNCTION
END IF
IniFormatSection$ = section$
END FUNCTION
FUNCTION ReadSetting$ (file$, __section$, __key$)
SHARED IniLastSection$, IniLastKey$, IniWholeFile$, IniLF$
SHARED IniPosition AS _UNSIGNED LONG, IniSectionData$
SHARED IniCODE, IniAllowBasicComments
SHARED currentIniFileLOF AS _UNSIGNED LONG
IniLoad file$
IF IniCODE THEN EXIT FUNCTION
IF currentIniFileLOF = 0 OR LEN(LTRIM$(RTRIM$(IniWholeFile$))) = 0 THEN IniCODE = 17: EXIT FUNCTION
DIM Equal AS _UNSIGNED LONG, tempValue$, key$, section$
DIM Quote AS _UNSIGNED LONG, Comment AS _UNSIGNED LONG
DIM i AS LONG, FoundLF AS _UNSIGNED LONG
section$ = IniFormatSection(__section$)
IF IniCODE THEN EXIT FUNCTION
'fetch the desired section$
IniSectionData$ = IniGetSection(section$)
IF IniCODE > 0 AND IniCODE <> 17 THEN EXIT FUNCTION
IF LEN(IniSectionData$) = 0 AND section$ <> "[]" THEN IniCODE = 14: EXIT FUNCTION
IniLastSection$ = section$
IniPosition = 0
key$ = LTRIM$(RTRIM$(__key$))
IniLastKey$ = ""
IF key$ = "" THEN
IF section$ = "[]" THEN IniSectionData$ = IniWholeFile$
key$ = IniNextKey
IF IniCODE THEN EXIT FUNCTION
IF key$ = "" THEN
IniCODE = 10
EXIT FUNCTION
END IF
END IF
IF LEFT$(key$, 1) = ";" OR LEFT$(key$, 1) = "'" OR INSTR(key$, "[") > 0 OR INSTR(key$, "]") > 0 OR INSTR(key$, "=") > 0 THEN
IniCODE = 12
EXIT FUNCTION
END IF
IniLastKey$ = key$
IF IniPosition > 0 THEN Equal = IniPosition: GOTO KeyFound
CheckKey:
IniPosition = INSTR(IniPosition + 1, LCASE$(IniSectionData$), LCASE$(key$))
IF IniPosition > 0 THEN
'identify if this occurrence is actually a key and not part of a key name/value
FOR i = IniPosition - 1 TO 1 STEP -1
IF ASC(IniSectionData$, i) = 10 THEN EXIT FOR
IF ASC(IniSectionData$, i) <> 10 AND ASC(IniSectionData$, i) <> 32 THEN
'not a key
GOTO CheckKey
END IF
NEXT
'check if there's nothing but an equal sign ahead
FOR i = IniPosition + LEN(key$) TO LEN(IniSectionData$)
IF ASC(IniSectionData$, i) = ASC("=") THEN EXIT FOR
IF ASC(IniSectionData$, i) <> ASC("=") AND ASC(IniSectionData$, i) <> 32 THEN
'not the key
GOTO CheckKey
END IF
NEXT
'so far so good; check if there is an assignment
Equal = INSTR(IniPosition, IniSectionData$, "=")
KeyFound:
FoundLF = INSTR(IniPosition, IniSectionData$, IniLF$)
IF FoundLF > 0 THEN
IF Equal > FoundLF THEN GOTO CheckKey
ELSE
FoundLF = LEN(IniSectionData$) + 1
IF Equal = 0 THEN GOTO CheckKey
END IF
tempValue$ = LTRIM$(RTRIM$(MID$(IniSectionData$, Equal + 1, FoundLF - Equal - 1)))
IF LEN(tempValue$) > 0 THEN
IF LEFT$(tempValue$, 1) = CHR$(34) THEN
tempValue$ = MID$(tempValue$, 2)
Quote = INSTR(tempValue$, CHR$(34))
IF Quote > 0 THEN
tempValue$ = LEFT$(tempValue$, Quote - 1)
END IF
ELSE
IF IniAllowBasicComments THEN Comment = INSTR(tempValue$, "'") 'BASIC style comments accepted
IF Comment = 0 THEN Comment = INSTR(tempValue$, ";")
IF Comment > 0 THEN
tempValue$ = LTRIM$(RTRIM$(LEFT$(tempValue$, Comment - 1)))
END IF
END IF
ELSE
IniCODE = 2
END IF
ELSE
IniCODE = 3
EXIT FUNCTION
END IF
ReadSetting$ = tempValue$
IniLastSection$ = IniCurrentSection$
END FUNCTION
FUNCTION IniCurrentSection$
SHARED IniPosition AS _UNSIGNED LONG, IniSectionData$, IniWholeFile$
DIM GlobalPosition AS _UNSIGNED LONG, i AS _UNSIGNED LONG
DIM ClosingBracket AS _UNSIGNED LONG
GlobalPosition = INSTR(IniWholeFile$, IniSectionData$) + IniPosition - 1
CheckSection:
FOR i = GlobalPosition - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = ASC("[") THEN
GlobalPosition = i: EXIT FOR
END IF
NEXT
IF i = 0 THEN IniCurrentSection$ = "[]": EXIT FUNCTION
'identify if this occurrence is actually a section header and not something else
FOR i = GlobalPosition - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN EXIT FOR
IF ASC(IniWholeFile$, i) <> 10 AND ASC(IniWholeFile$, i) <> 32 THEN
'not a section header
GOTO CheckSection
END IF
NEXT
ClosingBracket = INSTR(GlobalPosition, IniWholeFile$, "]")
IF ClosingBracket > 0 THEN
IniCurrentSection$ = MID$(IniWholeFile$, GlobalPosition, ClosingBracket - GlobalPosition + 1)
END IF
END FUNCTION
SUB WriteSetting (file$, __section$, __key$, __value$)
SHARED IniPosition AS _UNSIGNED LONG, IniCODE, currentIniFileName$
SHARED IniLF$, IniWholeFile$, IniSectionData$
SHARED IniLastSection$, IniLastKey$, IniNewFile$
DIM tempValue$, section$, key$, value$
IniCODE = 0
'prepare variables for the write operation
section$ = IniFormatSection$(__section$)
IF IniCODE THEN EXIT SUB
key$ = LTRIM$(RTRIM$(__key$))
IF key$ = "" THEN IniCODE = 12: EXIT SUB
IniLastKey$ = key$
value$ = LTRIM$(RTRIM$(__value$))
IF LTRIM$(STR$(VAL(value$))) <> value$ THEN
'if not a numeric value and value contains spaces, add quotation marks
IF INSTR(value$, CHR$(32)) THEN value$ = CHR$(34) + value$ + CHR$(34)
END IF
'Read the existing key to fill IniPosition
tempValue$ = ReadSetting$(file$, section$, key$)
'map IniPosition (set in the section block) to the global file position
IniPosition = INSTR(IniWholeFile$, IniSectionData$) + IniPosition - 1
IF IniCODE = 1 OR IniCODE = 17 THEN
'file not found or empty; create a new one
IF file$ = "" THEN file$ = currentIniFileName$
IF file$ = "" THEN IniCODE = 21: EXIT SUB
currentIniFileName$ = file$
IF section$ <> "[]" THEN
IniNewFile$ = section$ + IniLF$
END IF
IniNewFile$ = IniNewFile$ + key$ + "=" + value$
IniCODE = 0
IniCommit
IniLoad file$
IF IniCODE = 0 THEN IniCODE = 11
IniLastSection$ = section$
EXIT SUB
END IF
IF IniCODE = 0 OR IniCODE = 2 THEN 'key found and read back; write new value$
IF LCASE$(IniLastSection$) = LCASE$(section$) THEN
IF LTRIM$(RTRIM$(__value$)) = tempValue$ AND LEN(LTRIM$(RTRIM$(__value$))) > 0 THEN
'identical values skip the writing routine
IniCODE = 8
EXIT SUB
END IF
DIM nextLine AS _UNSIGNED LONG
nextLine = INSTR(IniPosition + 1, IniWholeFile$, IniLF$)
'create new file contents
IniNewFile$ = LEFT$(IniWholeFile$, IniPosition - 1)
IniNewFile$ = IniNewFile$ + key$ + "=" + value$
IF nextLine > 0 THEN
IniNewFile$ = IniNewFile$ + MID$(IniWholeFile$, nextLine)
END IF
IniCommit
IniCODE = 4
END IF
ELSEIF IniCODE = 3 OR IniCODE = 14 THEN 'Key not found, Section not found
IniCODE = 0
IF LCASE$(IniLastSection$) = LCASE$(section$) THEN
'find this section$ in the current ini file;
DIM Bracket1 AS _UNSIGNED LONG
DIM beginSection AS _UNSIGNED LONG, endSection AS _UNSIGNED LONG
DIM i AS _UNSIGNED LONG
beginSection = 0
endSection = 0
CheckSection:
beginSection = INSTR(beginSection + 1, LCASE$(IniWholeFile$), LCASE$(section$))
IF beginSection = 0 THEN GOTO CreateSection
'identify if this occurrence is actually the section header and not something else
FOR i = beginSection - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN EXIT FOR
IF ASC(IniWholeFile$, i) <> 10 AND ASC(IniWholeFile$, i) <> 32 THEN
'not the section header
GOTO CheckSection
END IF
NEXT
'we found it; time to identify where this section ends
'(either another [section], a blank line or the end of the file
Bracket1 = INSTR(beginSection + 1, IniWholeFile$, "[")
IF Bracket1 > 0 THEN
FOR i = Bracket1 - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN endSection = i + 1 - LEN(IniLF$): EXIT FOR
IF i <= beginSection THEN EXIT FOR
NEXT
END IF
IF endSection > 0 THEN
'add values to the end of the specified section$
IniNewFile$ = LEFT$(IniWholeFile$, endSection - 1)
IniNewFile$ = IniNewFile$ + key$ + "=" + value$ + IniLF$
IF MID$(IniWholeFile$, endSection, LEN(IniLF$)) <> IniLF$ THEN IniNewFile$ = IniNewFile$ + IniLF$
IniNewFile$ = IniNewFile$ + MID$(IniWholeFile$, endSection)
ELSE
'add values to the end of the file
IniNewFile$ = IniWholeFile$
IF RIGHT$(IniNewFile$, LEN(IniLF$)) = IniLF$ THEN
IniNewFile$ = IniNewFile$ + key$ + "=" + value$
ELSE
IniNewFile$ = IniNewFile$ + IniLF$ + key$ + "=" + value$
END IF
END IF
IniCommit
IF IniCODE = 0 THEN IniCODE = 7
EXIT SUB
ELSE
CreateSection:
IniNewFile$ = IniWholeFile$
IF section$ = "[]" THEN GOTO WriteAtTop
IF RIGHT$(IniNewFile$, LEN(IniLF$) * 2) = IniLF$ + IniLF$ THEN
IniNewFile$ = IniNewFile$ + section$ + IniLF$ + key$ + "=" + value$ + IniLF$
ELSEIF RIGHT$(IniNewFile$, LEN(IniLF$)) = IniLF$ THEN
IniNewFile$ = IniNewFile$ + IniLF$ + section$ + IniLF$ + key$ + "=" + value$ + IniLF$
ELSE
IniNewFile$ = IniNewFile$ + IniLF$ + IniLF$ + section$ + IniLF$ + key$ + "=" + value$ + IniLF$
END IF
IniCommit
IF IniCODE = 0 THEN IniCODE = 9 ELSE IniCODE = 16
EXIT SUB
END IF
'if not found, key$=value$ is written to the beginning of the file
WriteAtTop:
IniNewFile$ = key$ + "=" + value$ + IniLF$
IF LEFT$(LTRIM$(IniWholeFile$), 1) = "[" THEN IniNewFile$ = IniNewFile$ + IniLF$
IniNewFile$ = IniNewFile$ + IniWholeFile$
IniCommit
IniCODE = 5
END IF
END SUB
SUB IniSetForceReload (state AS _BYTE)
SHARED IniForceReload
IF state THEN
IniForceReload = -1
ELSE
IniForceReload = 0
END IF
END SUB
SUB IniClose
SHARED IniDisableAutoCommit, currentIniFileName$
SHARED LoadedFiles$
DIM findFile AS INTEGER, fileNum AS INTEGER
IF currentIniFileName$ = "" THEN EXIT SUB
'search LoadedFiles$, so we use the same file handle every time
findFile = INSTR(LoadedFiles$, "@" + currentIniFileName$ + "@")
IF findFile = 0 THEN
'not open; nothing to close
EXIT SUB
ELSE
fileNum = CVI(MID$(LoadedFiles$, findFile - 2, 2))
CLOSE fileNum
END IF
IniDisableAutoCommit = 0
IniCommit
currentIniFileName$ = ""
END SUB
SUB IniLoad (file$)
SHARED IniCODE, currentIniFileName$, IniLF$, IniWholeFile$
SHARED currentIniFileLOF AS _UNSIGNED LONG
SHARED IniForceReload
SHARED LoadedFiles$
DIM fileNum AS INTEGER, findFile AS INTEGER
'Error messages are returned with IniCODE
'Error descriptions can be fetched with function IniINFO$
IniCODE = 0
IF file$ <> "" AND currentIniFileName$ <> file$ THEN currentIniFileName$ = ""
IF IniForceReload AND LEN(currentIniFileName$) > 0 THEN
file$ = currentIniFileName$
currentIniFileName$ = ""
END IF
'Passing an empty file$ is allowed if user already
'passed a valid file in this session.
IF currentIniFileName$ = "" THEN
'initialization
IF _FILEEXISTS(file$) THEN
currentIniFileName$ = file$
'add to LoadedFiles$, so we use the same file handle every time
findFile = INSTR(LoadedFiles$, "@" + file$ + "@")
IF findFile = 0 THEN
fileNum = FREEFILE
LoadedFiles$ = LoadedFiles$ + "@" + MKI$(fileNum) + "@" + file$ + "@"
ELSE
fileNum = CVI(MID$(LoadedFiles$, findFile - 2, 2))
END IF
'Load file into memory
CLOSE fileNum
OPEN currentIniFileName$ FOR BINARY AS #fileNum
currentIniFileLOF = LOF(fileNum)
IniWholeFile$ = SPACE$(currentIniFileLOF)
GET #fileNum, 1, IniWholeFile$
'Check if this ini file uses CRLF or LF
IF INSTR(IniWholeFile$, CHR$(13)) THEN IniLF$ = CHR$(13) + CHR$(10) ELSE IniLF$ = CHR$(10)
ELSE
IniFileNotFound:
IniCODE = 1
$IF WIN THEN
IniLF$ = CHR$(13) + CHR$(10)
$ELSE
IniLF$ = CHR$(10)
$END IF
EXIT SUB
END IF
ELSEIF NOT _FILEEXISTS(currentIniFileName$) THEN
currentIniFileName$ = ""
GOTO IniFileNotFound
END IF
END SUB
FUNCTION IniNextKey$
SHARED IniCODE, IniLF$, currentIniFileName$, IniSectionData$
SHARED IniPosition AS _UNSIGNED LONG
STATIC lastDataBlock$, position AS _UNSIGNED LONG, tempLF$
IF currentIniFileName$ = "" THEN IniCODE = 18: EXIT FUNCTION
IF IniSectionData$ <> lastDataBlock$ THEN
position = 0
lastDataBlock$ = IniSectionData$
'data blocks must end with a line feed for parsing purposes
IF RIGHT$(IniSectionData$, LEN(IniLF$)) <> IniLF$ THEN tempLF$ = IniLF$ ELSE tempLF$ = ""
END IF
DIM Equal AS _UNSIGNED LONG, tempKey$
FindKey:
Equal = INSTR(position, IniSectionData$ + tempLF$, "=")
IF Equal = 0 THEN position = 0: EXIT FUNCTION
tempKey$ = LTRIM$(RTRIM$(MID$(IniSectionData$ + tempLF$, position + 1, Equal - position - 1)))
IF INSTR(tempKey$, CHR$(10)) > 0 THEN
position = position + INSTR(tempKey$, CHR$(10)) + 1
tempKey$ = MID$(tempKey$, INSTR(tempKey$, CHR$(10)) + 1)
END IF
DO WHILE LEFT$(tempKey$, LEN(IniLF$)) = IniLF$
tempKey$ = MID$(tempKey$, LEN(IniLF$) + 1)
position = position + LEN(IniLF$)
LOOP
position = INSTR(position + 1, IniSectionData$ + tempLF$, IniLF$)
IF LEFT$(tempKey$, 1) = ";" OR LEFT$(tempKey$, 1) = "'" OR INSTR(tempKey$, "[") > 0 OR INSTR(tempKey$, "]") > 0 OR INSTR(tempKey$, "=") > 0 THEN
GOTO FindKey
END IF
IniNextKey$ = tempKey$
IniPosition = Equal
END FUNCTION
FUNCTION IniNextSection$ (file$)
SHARED IniCODE, IniLF$, IniWholeFile$
STATIC sectionStart AS _UNSIGNED LONG
IniLoad file$
IF LEFT$(IniINFO$, 6) = "ERROR:" THEN EXIT FUNCTION
IniCODE = 0
DIM foundSection AS _UNSIGNED LONG, endSection AS _UNSIGNED LONG
DIM i AS _UNSIGNED LONG, Bracket1 AS _UNSIGNED LONG, Bracket2 AS _UNSIGNED LONG
FindNext:
sectionStart = INSTR(sectionStart + 1, IniWholeFile$, "[")
IF sectionStart = 0 THEN IniCODE = 24: EXIT FUNCTION
'make sure it's a valid section header
foundSection = 0
FOR i = sectionStart - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN foundSection = i + 1: EXIT FOR
IF ASC(IniWholeFile$, i) <> 32 THEN GOTO FindNext
NEXT
IF i = 0 THEN foundSection = 1
IF foundSection > 0 THEN
'we found it; time to identify where this section ends
'(either another [section] or the end of the file
Bracket2 = INSTR(sectionStart + 1, IniWholeFile$, "]")
IF Bracket2 = 0 THEN IniCODE = 24: EXIT FUNCTION
Bracket1 = INSTR(sectionStart + 1, IniWholeFile$, "[")
IF Bracket1 > 0 THEN
FOR i = Bracket1 - 1 TO 1 STEP -1
IF ASC(IniWholeFile$, i) = 10 THEN endSection = i + 1 - LEN(IniLF$): EXIT FOR
IF i <= foundSection THEN EXIT FOR
NEXT
IniNextSection$ = MID$(IniWholeFile$, foundSection, Bracket2 - foundSection + 1)
ELSE
IniNextSection$ = MID$(IniWholeFile$, foundSection, Bracket2 - foundSection + 1)
IniCODE = 24
sectionStart = 0
END IF
ELSE
IniCODE = 24
END IF
END FUNCTION
FUNCTION IniINFO$
SHARED IniCODE
SELECT CASE IniCODE
CASE 0: IniINFO$ = "Operation successful"
CASE 1: IniINFO$ = "ERROR: File not found"
CASE 2: IniINFO$ = "Empty value"
CASE 3: IniINFO$ = "ERROR: Key not found"
CASE 4: IniINFO$ = "Key updated"
CASE 5: IniINFO$ = "Global key created"
CASE 7: IniINFO$ = "Key created in existing section"
CASE 8: IniINFO$ = "No changes applied (same value)"
CASE 9: IniINFO$ = "New section created; key created"
CASE 10: IniINFO$ = "No more keys"
CASE 11: IniINFO$ = "File created; new key added"
CASE 12: IniINFO$ = "ERROR: Invalid key"
CASE 13: IniINFO$ = "Section deleted"
CASE 14: IniINFO$ = "ERROR: Section not found"
CASE 15: IniINFO$ = "ERROR: Invalid section"
CASE 16: IniINFO$ = "New section created; existing key moved"
CASE 17: IniINFO$ = "ERROR: Empty file"
CASE 18: IniINFO$ = "ERROR: No file open"
CASE 19: IniINFO$ = "Key deleted"
CASE 20: IniINFO$ = "Key moved"
CASE 21: IniINFO$ = "ERROR: Invalid file name/path"
CASE 22: IniINFO$ = "Section sorted"
CASE 23: IniINFO$ = "No changes applied; section already sorted"
CASE 24: IniINFO$ = "No more sections"
CASE ELSE: IniINFO$ = "ERROR: <invalid error code>"
END SELECT
END FUNCTION
'Written in BASIC by Luke Ceddia for ide_methods.bas (QB64)
'After Cormen, Leiserson, Rivest & Stein "Introduction To Algoritms" via Wikipedia
'Adapted for use in .INI Manager
FUNCTION IniArraySort%% (arr() AS STRING)
DIM i&, x$, j&, moves&
FOR i& = LBOUND(arr) + 1 TO UBOUND(arr)
x$ = arr(i&)
j& = i& - 1
WHILE j& >= LBOUND(arr)
IF arr(j&) <= x$ THEN EXIT WHILE
moves& = moves& + 1
arr$(j& + 1) = arr$(j&)
j& = j& - 1
WEND
arr$(j& + 1) = x$
NEXT i&
'Returns -1 (true) if any changes were made
IniArraySort%% = moves& > 0
END FUNCTION
$END IF