2 # Dbfreade package - reads dbf file
6 # Dbf files seldom use KOI8
10 # Read header of file and return list of fields. Also fill global array
11 # with name same as file handle
13 proc dbf_readheader {file} {
15 fconfigure $file -translation binary
16 set rec [read $file 32]
17 binary scan $rec "cccciss" info(version) year month day info(reccount) info(offset) info(reclen)
18 set info(datestamp) [clock scan "$month/$day/$year"]
19 set info(fieldcount) [expr {($info(offset)-1)/32-1}]
21 for {set i 1} {$i<=$info(fieldcount)} {incr i} {
22 set rec [read $file 32]
23 binary scan $rec "a11ax4cc" info(name,$i) info(type,$i) info(len,$i) \
25 set info(ofs,$i) $recoffset
26 incr recoffset $info(len,$i)
31 # Opens DBF file with given name.
32 # Fills all neccesary information
33 # and returns file descriptor
35 proc opendbf filename {
37 if [catch {open $filename} res] {
38 return -code error -errorcode $errorCode $res
44 # Position dbf file to given record
46 proc dbf_seek {file recno} {
48 if {![array exists info]||![info exists info(version)]} {
49 return -code error "No structure information exists for $file"
51 if {$recno>$info(reccount)||$recno<0} {
52 return -code error "No such record: $recno"
54 seek $file [expr {$info(offset)+$info(reclen)*(int($recno)-1)}]
60 proc dbf_getrec {file} {
62 if {![array exists info]||![info exists info(version)]} {
63 return -code error "No structure information exists for $file"
65 set rec [read $file $info(reclen)]
69 for {set i 1} {$i<=$info(fieldcount)} {incr i} {
72 set tmp [string trim [string range $rec $first $last]]
74 switch "$info(type,$i)" {
76 if [regexp {([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])} \
82 if ![string length $tmp] {
86 "L" { if [string length $tmp] {
87 set tmp [regexp {^[YyTt1]} $tmp]
91 set tmp [recode -input cp866 $tmp]
100 # checkdbf - checks if structure matches given list of fields and types
101 # Raises error and returns message if something doesn't match
103 proc checkdbf {file fieldlist typelist } {
106 foreach f $fieldlist t $typelist {
107 if {![info exists info(name,$i)]} {
108 return -code error "Not enough fields. $f expected"
110 if {"$info(name,$i)"!="$f"} {
111 return -code error "Field # $i doesn't match: $info(name,$i) while $f expected"
113 if {"$info(type,$i)"!="[lindex $t 0]"} {
114 return -code error "Field $f type mismatch $info(type,$i) found [lindex $t 0] expected."
116 if {$info(len,$i)!=[lindex $t 1]} {
117 return -code error "Field $f size mismatch $info(len,$i) found [lindex $t 1] expected."
119 if {"$info(type,$i)"=="N"&&[lindex $t 2]!=$info(dec,$i)} {
120 return -code error "Field $f decimal places mismatch $info(dec,$i) found [lindex $t 2] expected."
124 if {$i<=$info(fieldcount)} {
125 return -code error "Extra field $info(name,$i)"
131 # reccount - returns count of records
135 if ![info exist info(reccount)] {
136 return -code error "$f is not a dBase file"
138 return $info(reccount)
142 # fieldlist - return list of fields
144 proc fieldlist file {
147 for {set i 1} {$i<=$info(fieldcount)} {incr i} {
148 lappend list $info(name,$i)
154 # closedbf - closes file and frees data structure
156 proc closedbf {file} {
158 if ![uplevel #0 info exists $file] {
159 return -code error "$file is not a dBase file"
161 if [catch {close $file} msg] {
162 return -code error -errorcode $errorCode $msh
164 uplevel #0 unset $file
167 # scandbf file script
168 # executes script for each record in the file, appending record number
169 # and all field values to it.
170 proc scandbf {file script} {
174 while {$n<=$info(reccount)} {
175 uplevel $script $n [dbf_getrec $file]
180 package provide Dbfreader "1.0"