#
# 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

