# # Package: cfg # # Description: # Package of utilities for in-memory configuration file routines # # Provides the following functions: # # cfg create <name> - Create a cfg handle with name <name> # cfg create %AUTO% - Create a cfg handle # cfg list - List all open cfg handles # # <handle> read <filename> # <handle> write <filename> # <handle> destroy # <handle> show # # <handle> get <section> <key> # <handle> set <section> <key> <value> # <handle> delete <section> <key> # <handle> exists <section> <key> # # <handle> sections # <handle> section get <section> # <handle> section set <section> <key> <val> ?<key> <value>? ... # <handle> section delete <section> # <handle> section exists <section> # # Author: David Easton # Date: Nov 2003 # package provide cfg 1.0 namespace eval cfg { package require snit snit::type cfg { typevariable cfgList [list] typemethod list {} { return $cfgList } # Used for efficient lreplace proc K {x y} { set x } # Define variable in which to store the cfg information variable data variable sectionList constructor {args} { lappend cfgList $self set sectionList [list] } destructor { if {[set i [lsearch $cfgList $self]] != -1} { set cfgList [lreplace $cfgList $i $i] } } # # Private methods # method AddSection {section} { if {[lsearch $sectionList $section] == -1} { lappend sectionList $section set data($section:keyList) [list] } } method DelSection {section} { if {[set i [lsearch $sectionList $section]] != -1} { set sectionList [lreplace $sectionList $i $i] foreach entry [array names data $section,*] { unset data($entry) } unset data($section:keyList) } } method AddKey {section key} { $self AddSection $section if {[lsearch $data($section:keyList) $key] == -1} { lappend data($section:keyList) $key } } method DelKey {section key} { if {[set i [lsearch $data($section:keyList) $key]] != -1} { set data($section:keyList) [lreplace $data($section:keyList) $i $i] unset data($section,$key) } } # # Public methods # method read {filename} { set retCode ok set section "" if {[catch {open $filename r} fileId]} { set retCode "error" } else { # Read whole file as a list set dataList [split [read -nonewline $fileId] \n] close $fileId # Now process the list foreach line $dataList { # Ignore blank lines if {[string trim $line] == ""} {continue} # Ignore comments if {[string match "#*" $line]} {continue} # Match sections if {[regexp {\[(.*)\]} $line junk section]} { # We have reached a new section } if {[string match "*=*" $line]} { foreach {key val} [split $line =] {break} $self set $section $key $val } } } return -code $retCode } method write {filename} { set retCode "ok" # Make parent directories if needed if {![info exists [file dirname $filename]]} { file mkdir [file dirname $filename] } # Open file for writing if {[catch {open $filename w} fid]} { set retCode "error" } else { foreach section $sectionList { puts $fid "\[$section\]" foreach key $data($section:keyList) { puts $fid "$key=$data($section,$key)" } puts $fid "" } flush $fid close $fid } } method sections {} { return $sectionList } method get {section key} { set value "" if [info exists data($section,$key)] { set value $data($section,$key) } return $value } method set {section key value} { if {![info exists data($section,$key)]} { $self AddKey $section $key } set data($section,$key) $value } method delete {section key} { $self DelKey $section $key } method exists {section key} { return [info exists data($section,$key)] } method show {} { foreach section $sectionList { puts "\[$section\]" foreach key $data($section:keyList) { puts "$key=$data($section,$key)" } puts "" } } method section {args} { if {[llength $args] < 2} { set message "wrong # args: should be \"$self section <option> <section> ?args?\"" return -code error $message } else { set cmd [lindex $args 0] set section [lindex $args 1] set args [lrange $args 2 end] } switch -- $cmd { get { set res [list] foreach key $data($section:keyList) { lappend res $key $data($section,$key) } return $res } set { set keyValList [concat $args] set message [list] foreach {key value} $keyValList { lappend message [$self set $section $key $value] } return $message } exists { if {[lsearch $sectionList $section] == -1} { return 0 } else { return 1 } } list { return $data($section:keyList) } delete { return [$self DelSection $section] } default { set message "bad option $cmd: must be get, set, exists, list, delete" return -code error $message } } } } }
Here is how it can be used:
package require cfg # Create a example.cfg config file set cfg [cfg::cfg create %AUTO%] $cfg set "General" attribute "This is my value" $cfg write example.cfg $cfg destroy # Read the config file set cfg2 [cfg::cfg create %AUTO%] $cfg2 read example.cfg puts "Value is: [$cfg2 get General attribute]" # Show the config file $cfg2 show # Now free it $cfg2 destroy