Markus Tzoe avatar Markus Tzoe committed 5c56cd2

fundl version; impl inspired by db_dump.c from dbview

Comments (0)

Files changed (1)

+/*
+    Ported to Go Lang, by Marcus Zy <chou.marcus@gmail.com>
+    10-04-2012
+*/
+/*
+ * These functions are used to read dbase files.
+ * *
+ * The program is for test purposes only.  No warranty is expressed nor
+ * implied. USE AT YOUR OWN RISK!
+ *
+ *
+ */
+
+package dbase
+
+import (
+    "io/ioutil"
+    "fmt"
+    "encoding/binary"
+)
+
+const (
+    DB_FL_BROWSE    = 0x01
+    DB_FL_INFO      = 0x02
+    DB_FL_DESCR     = 0x04
+    DB_FL_RESERVE   = 0x08
+    DB_FL_OMIT      = 0x10
+    DB_FL_TRIM      = 0x20
+    DB_FL_DELETED   = 0x40
+    DB_FLD_CHAR     = 'C'
+    DB_FLD_NUM      = 'N'
+    DB_FLD_LOGIC    = 'L'
+    DB_FLD_MEMO     = 'M'
+    DB_FLD_DATE     = 'D'
+    DB_FILE_SYS     = "/usr/local/share/MyIP.dat"
+)
+
+type DBaseHead struct {
+    version uint8       /* 03 for dbIII and 83 for dbIII w/memo file */
+    l_update []uint8    /* yymmdd for last update: size 3*/
+    count uint32        /* number of records in file*/
+    header uint16       /* length of the header, includes the \r at end */
+    lrecl uint16        /* length of a record, includes the delete byte */
+    reserv []uint8      /* size 20 */
+}
+
+type DBaseField struct {
+    name []uint8        /*field name: length 11*/
+    genre uint8         /*field type*/
+                        /* A-T uses large data model but drop it for now */
+    data_ptr uintptr    /*pointer into buffer*/
+    length uint8        /*field length*/
+    dec_point uint8     /*field decimal point*/
+    fill []uint8        /* length 14 */
+}
+
+type DBaseRecord struct {
+    data []byte
+}
+
+type DBaseFile struct {
+    head DBaseHead
+    base []byte
+    fields []DBaseField
+    entry []DBaseRecord
+}
+
+func NewReader(path string) (ret *DBaseFile, err error) {
+    ret = new(DBaseFile)
+    ret.base, err = ioutil.ReadFile(path)
+    if err != nil {
+        return nil, err
+    }
+    ret.readHead()
+    _,err = ret.verify()
+    if err != nil {
+        return nil, err
+    }
+    nf := (ret.head.header - 1) / 32 - 1
+    ret.fields = make([]DBaseField, nf)
+    ret.readFields(nf)
+    ret.entry = make([]DBaseRecord, ret.head.count)
+    return ret, nil
+}
+
+func (f *DBaseFile) verify() (bool, error) {
+    if !(f.head.version == 3 || f.head.version == 0x83) {
+        if f.head.version == 0x8b {
+            err := fmt.Errorf("dBase IV version %d - partially known...", f.head.version)
+            return false, err
+        }
+        err := fmt.Errorf("Unsupported version %d", f.head.version)
+        return false, err
+    }
+    return true, nil
+}
+
+func (f *DBaseFile) readHead() {
+    f.head.version = read1byte(f.base, 0x00)
+    f.head.l_update = f.base[0x01:0x04]
+    f.head.count = read4byte(f.base, 0x04)
+    f.head.header = read2byte(f.base, 0x08)
+    f.head.lrecl = read2byte(f.base, 0x0A)
+    f.head.reserv = f.base[0x0C:0x20]
+}
+
+func (f *DBaseFile) readFields(nf uint16) {
+    cur := uint32(0x20)
+    for i := uint16(0); i < nf; i++ {
+        f.fields[i].name = f.base[cur:cur+11]
+        f.fields[i].genre = read1byte(f.base, cur+11)
+        f.fields[i].data_ptr = (uintptr)(read4byte(f.base, cur+12))
+        f.fields[i].length = read1byte(f.base, cur+16)
+        f.fields[i].dec_point = read1byte(f.base, cur+17)
+        f.fields[i].fill = f.base[cur+18:cur+32]
+        cur += 0x20
+    }
+}
+
+func read1byte(base []byte, offset uint32) (ret uint8) {
+    ret = base[offset]
+    return
+}
+
+func read2byte(base []byte, offset uint32) (ret uint16) {
+    r := base[offset:offset+4]
+    ret = uint16(binary.LittleEndian.Uint32(r))
+    return
+}
+
+func read4byte(base []byte, offset uint32) (ret uint32) {
+    r := base[offset:offset+4]
+    ret = binary.LittleEndian.Uint32(r)
+    return
+}
+
+func (f *DBaseFile) Info() {
+    fmt.Printf("File version  : %d\n",f.head.version);
+    fmt.Printf("Last update   : %02d/%02d/%2d\n", f.head.l_update[1],f.head.l_update[2],uint16(f.head.l_update[0])+1900);
+    fmt.Printf("Number of recs: %d\n",f.head.count);
+    fmt.Printf("Header length : %d\n",f.head.header);
+    fmt.Printf("Record length : %d\n",f.head.lrecl);
+    
+    fmt.Printf("\nField Name\tType\tLength\tDecimal Pos\n")
+    for _,v := range f.fields {
+        fmt.Printf("%-16s\t  %c\t  %3d\t  %3d\n", v.name, v.genre, v.length,v.dec_point);
+    }
+    fmt.Printf("\n")
+}
+
+func (f *DBaseFile) Print(flags int, delim byte, deleted byte) {
+    var l uint8
+    var nf = len(f.fields)
+    var buffer []byte
+    for _, v := range f.entry {
+        buffer = v.data
+        for i := 0; i < nf; i++ {
+            l = f.fields[i].length
+            fmt.Printf("%-10s\t:%s\n", f.fields[i].name, buffer[:l])
+            buffer = buffer[l:]
+        }
+        fmt.Printf("\n")
+    }
+}
+
+func (f *DBaseFile) Process() {
+    offset := f.head.header
+    rlen := f.head.lrecl
+    data := f.base[offset:]
+    for cnt := f.head.count; cnt > 0; cnt-- {
+        f.entry[f.head.count-cnt].data = data[0:rlen]
+        data = data[rlen:]
+    }
+}
+
+/*func main() {
+    f, err := NewReader(DB_FILE_SYS)
+    if err != nil {
+        fmt.Println(err)
+        return 
+    }
+    f.Info()
+    f.Process()
+    //f.Print(0x01, 0x02, 0x03)
+}*/
+
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.