mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-01 21:00:15 +00:00
501 lines
17 KiB
Text
501 lines
17 KiB
Text
'INI Manager v1.01
|
|
'Fellippe Heitor, 2017-2021 - fellippe@qb64.org - @fellippeheitor
|
|
|
|
SUB IniCommit
|
|
SHARED currentIniFileName$, IniWholeFile$, currentIniFileLOF AS _UNSIGNED LONG
|
|
SHARED IniNewFile$, IniDisableAutoCommit, IniCODE
|
|
|
|
IF currentIniFileName$ = "" THEN IniCODE = 18: EXIT SUB
|
|
|
|
IniWholeFile$ = IniNewFile$
|
|
currentIniFileLOF = LEN(IniNewFile$)
|
|
|
|
IF NOT IniDisableAutoCommit THEN
|
|
DIM fileNum AS INTEGER
|
|
|
|
fileNum = FREEFILE
|
|
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
|
|
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 IniCODE = 12: EXIT FUNCTION
|
|
|
|
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$
|
|
SHARED IniDisableAddQuotes
|
|
|
|
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 IniDisableAddQuotes 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
|
|
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 IniSetAddQuotes (state AS _BYTE)
|
|
SHARED IniDisableAddQuotes
|
|
IF state THEN
|
|
IniDisableAddQuotes = 0
|
|
ELSE
|
|
IniDisableAddQuotes = -1
|
|
END IF
|
|
END SUB
|
|
|
|
SUB IniSetForceReload (state AS _BYTE)
|
|
SHARED IniForceReload
|
|
IF state THEN
|
|
IniForceReload = -1
|
|
ELSE
|
|
IniForceReload = 0
|
|
END IF
|
|
END SUB
|
|
|
|
SUB IniSetAllowBasicComments (state AS _BYTE)
|
|
SHARED IniAllowBasicComments
|
|
IF state THEN
|
|
IniAllowBasicComments = -1
|
|
ELSE
|
|
IniAllowBasicComments = 0
|
|
END IF
|
|
END SUB
|
|
|
|
SUB IniSetAutoCommit (state AS _BYTE)
|
|
SHARED IniDisableAutoCommit
|
|
IF state THEN
|
|
IniDisableAutoCommit = 0
|
|
ELSE
|
|
IniDisableAutoCommit = -1
|
|
END IF
|
|
END SUB
|
|
|
|
SUB IniLoad (file$)
|
|
SHARED IniCODE, currentIniFileName$, IniLF$, IniWholeFile$
|
|
SHARED currentIniFileLOF AS _UNSIGNED LONG
|
|
SHARED IniForceReload
|
|
DIM fileNum AS INTEGER
|
|
|
|
'Error messages are returned with IniCODE
|
|
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$
|
|
|
|
'Load file into memory
|
|
fileNum = FREEFILE
|
|
OPEN currentIniFileName$ FOR BINARY AS #fileNum
|
|
currentIniFileLOF = LOF(fileNum)
|
|
IniWholeFile$ = SPACE$(currentIniFileLOF)
|
|
GET #fileNum, 1, IniWholeFile$
|
|
CLOSE #fileNum
|
|
|
|
'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
|