ConfigLib — Configuration Library

Persistent key/value storage using INI files. Save user preferences, application settings, game progress, and other data that persists between program executions. Cross-platform with automatic path resolution.

CategoryCountDescription
File Creation2cfg_open#, cfg_open_auto#
String Operations4cfg_set#, cfg_get$, cfg_sets#, cfg_gets$
Numeric Operations4cfg_setn#, cfg_getn, cfg_setns#, cfg_getns
Boolean Operations4cfg_setb#, cfg_getb, cfg_setbs#, cfg_getbs
Key/Section Query3cfg_exists, cfg_haskey, cfg_section_exists
Key/Section Mgmt4cfg_delete#, cfg_deletekey#, cfg_section_delete#, cfg_clear#
Enumeration4cfg_sections$, cfg_keys$, cfg_sectioncount, cfg_keycount
File Operations5cfg_save, cfg_reload#, cfg_filename$, cfg_modified, cfg_autosave#
Utility1cfg_path$
FeatureDescription
INI FormatHuman-readable [Section] key=value text files
Default SectionEmpty string "" maps to the General section
Auto-Save ModeOptional immediate write after every change
Default ValuesAll get functions accept a default for missing keys
MemoryConfig objects are garbage-collected; unsaved changes saved on cleanup

Storage Locations

Config files are stored in platform-appropriate directories automatically.

PlatformPath
WindowsDocuments\Plan9Basic\Config\
Linux~/.config/Plan9Basic/
macOS~/Library/Application Support/Plan9Basic/
AndroidApp documents /Config/
iOSApp documents /Config/
╯ plan9basic
' Simple filename -> platform config dir + .ini
cfg1# = cfg_open#("settings")
' Windows: Documents\Plan9Basic\Config\settings.ini

' Full path used as-is
cfg2# = cfg_open#("/home/user/myconfig.ini")
ⓘ Note: If no extension is given, .ini is added automatically. Use cfg_path$() to get the config directory path.

INI File Format

╯ settings.ini
[General]
username=John
theme=dark

[Window]
width=800
height=600
maximized=0

[Recent]
file1=C:\Documents\report.txt
file2=C:\Documents\notes.txt
ElementFormat
SectionsEnclosed in square brackets: [SectionName]
Key/ValueSeparated by =
EncodingUTF-8
CommentsNot supported in the file format

Config File Functions

FunctionSignatureDescription
cfg_open#(filename$)cfg_open#@$Open/create config with manual save
cfg_open_auto#(filename$)cfg_open_auto#@$Open/create config with auto-save enabled
╯ plan9basic
' Manual save - changes written only on cfg_save()
cfg# = cfg_open#("myapp")
cfg_sets#(cfg#, "username", "John")
cfg_setns#(cfg#, "volume", 75)
cfg_save(cfg#)

' Auto-save - every change written immediately
cfg# = cfg_open_auto#("critical_settings")
cfg_sets#(cfg#, "lastSave", "2025-01-21")  ' saved instantly
cfg_setns#(cfg#, "counter", 42)             ' saved instantly
ModeBest For
cfg_open# (manual)Batch updates, Save/Cancel UI, many rapid changes
cfg_open_auto# (auto)Critical settings, simple apps, infrequent changes

String Operations

FunctionSignatureDescription
cfg_set#(c#, section$, key$, val$)cfg_set#@#$$$Set string in section
cfg_get$(c#, section$, key$, def$)cfg_get$@#$$$Get string from section (with default)
cfg_sets#(c#, key$, val$)cfg_sets#@#$$Set string in default (General) section
cfg_gets$(c#, key$, def$)cfg_gets$@#$$Get string from default section (with default)
╯ plan9basic
cfg# = cfg_open#("settings")

' With explicit section
cfg_set#(cfg#, "User", "name", "Alice")
cfg_set#(cfg#, "User", "email", "alice@example.com")
cfg_set#(cfg#, "Display", "theme", "dark")

name$ = cfg_get$(cfg#, "User", "name", "Guest")
theme$ = cfg_get$(cfg#, "Display", "theme", "light")

' Default section (General)
cfg_sets#(cfg#, "language", "en")
lang$ = cfg_gets$(cfg#, "language", "en")

cfg_save(cfg#)

Numeric Operations

FunctionSignatureDescription
cfg_setn#(c#, section$, key$, val)cfg_setn#@#$$nSet number in section
cfg_getn(c#, section$, key$, def)cfg_getn@#$$nGet number from section (with default)
cfg_setns#(c#, key$, val)cfg_setns#@#$nSet number in default section
cfg_getns(c#, key$, def)cfg_getns@#$nGet number from default section (with default)
╯ plan9basic
cfg# = cfg_open#("game")

' With section
cfg_setn#(cfg#, "Window", "width", 1024)
cfg_setn#(cfg#, "Window", "height", 768)
cfg_setn#(cfg#, "Audio", "volume", 0.8)

width = cfg_getn(cfg#, "Window", "width", 800)
volume = cfg_getn(cfg#, "Audio", "volume", 1.0)

' Default section
cfg_setns#(cfg#, "highScore", 15000)
best = cfg_getns(cfg#, "highScore", 0)

cfg_save(cfg#)

Boolean Operations

Booleans are stored as 0 (false) or 1 (true) in the INI file.

FunctionSignatureDescription
cfg_setb#(c#, section$, key$, val)cfg_setb#@#$$nSet boolean in section (0=false, non-zero=true)
cfg_getb(c#, section$, key$, def)cfg_getb@#$$nGet boolean from section (returns 1 or 0)
cfg_setbs#(c#, key$, val)cfg_setbs#@#$nSet boolean in default section
cfg_getbs(c#, key$, def)cfg_getbs@#$nGet boolean from default section
╯ plan9basic
cfg# = cfg_open#("prefs")

' With section
cfg_setb#(cfg#, "Features", "darkMode", 1)
cfg_setb#(cfg#, "Features", "notifications", 0)

darkMode = cfg_getb(cfg#, "Features", "darkMode", 0)
if darkMode = 1 then
    println "Dark mode enabled"
endif

' Default section - first run detection
firstRun = cfg_getbs(cfg#, "firstRun", 1)
if firstRun = 1 then
    println "Welcome! First time running the app."
    cfg_setbs#(cfg#, "firstRun", 0)
    cfg_save(cfg#)
endif

Key and Section Query

FunctionSignatureDescription
cfg_exists(c#, section$, key$)cfg_exists@#$$Key exists in section? (1/0)
cfg_haskey(c#, key$)cfg_haskey@#$Key exists in default section? (1/0)
cfg_section_exists(c#, section$)cfg_section_exists@#$Section exists? (1/0)
╯ plan9basic
cfg# = cfg_open#("settings")

if cfg_exists(cfg#, "User", "email") = 1 then
    println "Email: "; cfg_get$(cfg#, "User", "email", "")
endif

if cfg_haskey(cfg#, "license") = 1 then
    println "Licensed version"
else
    println "Trial version"
endif

if cfg_section_exists(cfg#, "SaveGame") = 1 then
    println "Save game found!"
else
    println "No save - starting new game"
endif

Key and Section Management

FunctionSignatureDescription
cfg_delete#(c#, section$, key$)cfg_delete#@#$$Delete key from section
cfg_deletekey#(c#, key$)cfg_deletekey#@#$Delete key from default section
cfg_section_delete#(c#, section$)cfg_section_delete#@#$Delete entire section and all its keys
cfg_clear#(c#)cfg_clear#@#Clear all sections and keys
╯ plan9basic
cfg# = cfg_open#("settings")

' Delete specific key
cfg_delete#(cfg#, "User", "temporaryToken")
cfg_deletekey#(cfg#, "tempData")

' Delete entire section
cfg_section_delete#(cfg#, "SaveGame")

' Reset everything
cfg_clear#(cfg#)
cfg_sets#(cfg#, "language", "en")
cfg_setns#(cfg#, "volume", 100)
cfg_save(cfg#)
println "Settings reset to defaults"

Enumeration Functions

FunctionSignatureDescription
cfg_sections$(c#)cfg_sections$@#Comma-separated list of all section names
cfg_keys$(c#, section$)cfg_keys$@#$Comma-separated list of keys in section
cfg_sectioncount(c#)cfg_sectioncount@#Number of sections
cfg_keycount(c#, section$)cfg_keycount@#$Number of keys in section
╯ plan9basic
cfg# = cfg_open#("settings")

println "Sections: "; cfg_sections$(cfg#)
' Output: General,User,Window,Audio

println "Section count: "; cfg_sectioncount(cfg#)

println "Window keys: "; cfg_keys$(cfg#, "Window")
' Output: width,height,x,y,maximized

println "Key count: "; cfg_keycount(cfg#, "User")

File Operations

FunctionSignatureDescription
cfg_save(c#)cfg_save@#Save to disk (1=success, 0=failure)
cfg_reload#(c#)cfg_reload#@#Reload from disk, discard unsaved changes
cfg_filename$(c#)cfg_filename$@#Full file path of config file
cfg_modified(c#)cfg_modified@#Unsaved changes? (1/0)
cfg_autosave#(c#, enabled)cfg_autosave#@#nEnable (1) or disable (0) auto-save
╯ plan9basic
cfg# = cfg_open#("settings")
cfg_sets#(cfg#, "theme", "dark")

' Check modified state
if cfg_modified(cfg#) = 1 then
    println "Unsaved changes!"
endif

' Save
if cfg_save(cfg#) = 1 then
    println "Saved to: "; cfg_filename$(cfg#)
else
    println "Save failed!"
endif

' Discard changes
cfg_sets#(cfg#, "theme", "experimental")
cfg_reload#(cfg#)
println "Changes discarded"

' Toggle auto-save
cfg_autosave#(cfg#, 1)
cfg_sets#(cfg#, "critical", "data")  ' saved instantly
cfg_autosave#(cfg#, 0)
cfg_sets#(cfg#, "batch1", "v1")
cfg_sets#(cfg#, "batch2", "v2")
cfg_save(cfg#)  ' save all at once

Utility Functions

FunctionSignatureDescription
cfg_path$()cfg_path$@Platform-specific config directory path
╯ plan9basic
println "Config dir: "; cfg_path$()
' Windows: ...\Documents\Plan9Basic\Config
' Linux:   ~/.config/Plan9Basic
' macOS:   ~/Library/Application Support/Plan9Basic

Complete Examples

Application Settings

╯ app_settings.bas
' Simple settings manager
cfg# = cfg_open_auto#("myapp")

if cfg_haskey(cfg#, "initialized") = 0 then
    println "First run - setting defaults..."
    cfg_sets#(cfg#, "initialized", "yes")
    cfg_sets#(cfg#, "language", "en")
    cfg_setns#(cfg#, "volume", 80)
    cfg_setbs#(cfg#, "fullscreen", 0)
endif

println "Language: "; cfg_gets$(cfg#, "language", "en")
println "Volume: "; cfg_getns(cfg#, "volume", 80)
println "Fullscreen: "; cfg_getbs(cfg#, "fullscreen", 0)
println "Config: "; cfg_filename$(cfg#)

Game Save System

╯ savegame.bas
' Save/load game with sections
cfg# = cfg_open#("savegame")

if cfg_section_exists(cfg#, "Player") = 1 then
    println "Loading saved game..."
    name$ = cfg_get$(cfg#, "Player", "name", "Hero")
    level = cfg_getn(cfg#, "Player", "level", 1)
    gold = cfg_getn(cfg#, "Player", "gold", 0)
    health = cfg_getn(cfg#, "Player", "health", 100)
else
    println "Starting new game..."
    name$ = "Hero"
    level = 1
    gold = 100
    health = 100
endif

println name$; " - Level "; level; ", Gold: "; gold; ", HP: "; health

' Simulate gameplay
gold = gold + 50
level = level + 1

' Save
cfg_set#(cfg#, "Player", "name", name$)
cfg_setn#(cfg#, "Player", "level", level)
cfg_setn#(cfg#, "Player", "gold", gold)
cfg_setn#(cfg#, "Player", "health", health)
cfg_save(cfg#)
println "Game saved!"

Window Position Memory

╯ winpos.bas
' Remember window position
cfg# = cfg_open#("window")

x = cfg_getn(cfg#, "Position", "x", 100)
y = cfg_getn(cfg#, "Position", "y", 100)
w = cfg_getn(cfg#, "Position", "width", 800)
h = cfg_getn(cfg#, "Position", "height", 600)
maximized = cfg_getb(cfg#, "Position", "maximized", 0)

println "Position: "; x; ", "; y
println "Size: "; w; " x "; h

' Save new position
cfg_setn#(cfg#, "Position", "x", x + 50)
cfg_setn#(cfg#, "Position", "y", y + 30)
cfg_save(cfg#)

Recent Files List

╯ recent.bas
' Manage recent files list
cfg# = cfg_open#("recent")

' Display current
println "Recent files:"
for i = 1 to 5
    key$ = "file" + stri$(i, 0)
    if cfg_exists(cfg#, "Recent", key$) = 1 then
        println "  "; i; ". "; cfg_get$(cfg#, "Recent", key$, "")
    endif
next

' Add new file (shift others down)
newFile$ = "C:\\Documents\\report_2025.txt"
for i = 4 to 1 step -1
    keyFrom$ = "file" + stri$(i, 0)
    keyTo$ = "file" + stri$(i + 1, 0)
    if cfg_exists(cfg#, "Recent", keyFrom$) = 1 then
        cfg_set#(cfg#, "Recent", keyTo$, cfg_get$(cfg#, "Recent", keyFrom$, ""))
    endif
next
cfg_set#(cfg#, "Recent", "file1", newFile$)
cfg_save(cfg#)

Multi-User Profiles

╯ profiles.bas
' User profiles with sections
cfg# = cfg_open#("profiles")

println "Sections: "; cfg_sections$(cfg#)

' Create / update a profile
profileName$ = "Player1"
cfg_set#(cfg#, profileName$, "displayName", "John Doe")
cfg_setn#(cfg#, profileName$, "highScore", 25000)
cfg_setn#(cfg#, profileName$, "gamesPlayed", 42)
cfg_setb#(cfg#, profileName$, "soundEnabled", 1)
cfg_save(cfg#)

println "Profile '"; profileName$; "':"
println "  Name: "; cfg_get$(cfg#, profileName$, "displayName", "?")
println "  Score: "; cfg_getn(cfg#, profileName$, "highScore", 0)
println "  Games: "; cfg_getn(cfg#, profileName$, "gamesPlayed", 0)

Configuration Dump

╯ dump.bas
' Dump all configuration data
cls
cfg# = cfg_open#("settings")

if cfg_sectioncount(cfg#) = 0 then
    cfg_sets#(cfg#, "appName", "TestApp")
    cfg_setns#(cfg#, "version", 1.5)
    cfg_set#(cfg#, "Database", "host", "localhost")
    cfg_setn#(cfg#, "Database", "port", 5432)
    cfg_save(cfg#)
endif

println "File: "; cfg_filename$(cfg#)
println "Sections: "; cfg_sectioncount(cfg#)
println "Modified: "; cfg_modified(cfg#)
println "Section list: "; cfg_sections$(cfg#)

Quick Reference

FunctionSignatureDescription
cfg_open#(f$) / cfg_open_auto#(f$)cfg_open*#@$Open config (manual / auto-save)
cfg_set#(c#, sec$, k$, v$)cfg_set#@#$$$Set string in section
cfg_get$(c#, sec$, k$, def$)cfg_get$@#$$$Get string from section
cfg_sets#(c#, k$, v$)cfg_sets#@#$$Set string (default section)
cfg_gets$(c#, k$, def$)cfg_gets$@#$$Get string (default section)
cfg_setn#(c#, sec$, k$, v)cfg_setn#@#$$nSet number in section
cfg_getn(c#, sec$, k$, def)cfg_getn@#$$nGet number from section
cfg_setns#(c#, k$, v)cfg_setns#@#$nSet number (default section)
cfg_getns(c#, k$, def)cfg_getns@#$nGet number (default section)
cfg_setb#(c#, sec$, k$, v)cfg_setb#@#$$nSet boolean in section
cfg_getb(c#, sec$, k$, def)cfg_getb@#$$nGet boolean from section
cfg_setbs#(c#, k$, v)cfg_setbs#@#$nSet boolean (default section)
cfg_getbs(c#, k$, def)cfg_getbs@#$nGet boolean (default section)
cfg_exists(c#, sec$, k$)cfg_exists@#$$Key exists in section? (1/0)
cfg_haskey(c#, k$)cfg_haskey@#$Key exists in default section? (1/0)
cfg_section_exists(c#, sec$)cfg_section_exists@#$Section exists? (1/0)
cfg_delete#(c#, sec$, k$)cfg_delete#@#$$Delete key from section
cfg_deletekey#(c#, k$)cfg_deletekey#@#$Delete key (default section)
cfg_section_delete#(c#, sec$)cfg_section_delete#@#$Delete entire section
cfg_clear#(c#)cfg_clear#@#Clear all data
cfg_sections$(c#)cfg_sections$@#List all sections (comma-separated)
cfg_keys$(c#, sec$)cfg_keys$@#$List keys in section
cfg_sectioncount(c#)cfg_sectioncount@#Number of sections
cfg_keycount(c#, sec$)cfg_keycount@#$Number of keys in section
cfg_save(c#)cfg_save@#Save to disk (1/0)
cfg_reload#(c#)cfg_reload#@#Reload from disk
cfg_filename$(c#)cfg_filename$@#Config file path
cfg_modified(c#)cfg_modified@#Unsaved changes? (1/0)
cfg_autosave#(c#, n)cfg_autosave#@#nEnable/disable auto-save
cfg_path$()cfg_path$@Config directory path

28 functions across 9 categories (+ 2 open modes).