pax_global_header00006660000000000000000000000064124712026660014520gustar00rootroot0000000000000052 comment=770cbe35261f55dbdcbf3681b9d9dbaea6ea216d cl-db3-20150302-git/000077500000000000000000000000001247120266600136035ustar00rootroot00000000000000cl-db3-20150302-git/README.md000066400000000000000000000077571247120266600151020ustar00rootroot00000000000000# DB3 A lib to read dbf files version 3. ## Database file structure The structure of a dBASE III database file is composed of a header and data records. The layout is given below. ### dBASE III DATABASE FILE HEADER: +---------+-------------------+---------------------------------+ | BYTE | CONTENTS | MEANING | +---------+-------------------+---------------------------------+ | 0 | 1 byte | dBASE III version number | | | | (03H without a .DBT file) | | | | (83H with a .DBT file) | +---------+-------------------+---------------------------------+ | 1-3 | 3 bytes | date of last update | | | | (YY MM DD) in binary format | +---------+-------------------+---------------------------------+ | 4-7 | 32 bit number | number of records in data file | +---------+-------------------+---------------------------------+ | 8-9 | 16 bit number | length of header structure | +---------+-------------------+---------------------------------+ | 10-11 | 16 bit number | length of the record | +---------+-------------------+---------------------------------+ | 12-31 | 20 bytes | reserved bytes (version 1.00) | +---------+-------------------+---------------------------------+ | 32-n | 32 bytes each | field descriptor array | | | | (see below) | --+ +---------+-------------------+---------------------------------+ | | n+1 | 1 byte | 0DH as the field terminator | | +---------+-------------------+---------------------------------+ | | | A FIELD DESCRIPTOR: <------------------------------------------+ +---------+-------------------+---------------------------------+ | BYTE | CONTENTS | MEANING | +---------+-------------------+---------------------------------+ | 0-10 | 11 bytes | field name in ASCII zero-filled | +---------+-------------------+---------------------------------+ | 11 | 1 byte | field type in ASCII | | | | (C N L D or M) | +---------+-------------------+---------------------------------+ | 12-15 | 32 bit number | field data address | | | | (address is set in memory) | +---------+-------------------+---------------------------------+ | 16 | 1 byte | field length in binary | +---------+-------------------+---------------------------------+ | 17 | 1 byte | field decimal count in binary | +---------+-------------------+---------------------------------+ | 18-31 | 14 bytes | reserved bytes (version 1.00) | +---------+-------------------+---------------------------------+ ### The data records are layed out as follows: 1. Data records are preceeded by one byte that is a space (20H) if the record is not deleted and an asterisk (2AH) if it is deleted. 2. Data fields are packed into records with no field separators or record terminators. 3. Data types are stored in ASCII format as follows: DATA TYPE DATA RECORD STORAGE --------- -------------------------------------------- Character (ASCII characters) Numeric - . 0 1 2 3 4 5 6 7 8 9 Logical ? Y y N n T t F f (? when not initialized) Memo (10 digits representing a .DBT block number) Date (8 digits in YYYYMMDD format, such as 19840704 for July 4, 1984) ----------------------------------------------------------- cl-db3-20150302-git/db3.asd000066400000000000000000000003331247120266600147430ustar00rootroot00000000000000;;;; abnf.asd (asdf:defsystem #:db3 :serial t :description "DB3 file reader" :author "Xach" :license "WTFPL" :depends-on () :components ((:file "package") (:file "db3" :depends-on ("package")))) cl-db3-20150302-git/db3.lisp000066400000000000000000000203761247120266600151540ustar00rootroot00000000000000;;;; http://xach.com/lisp/db3.lisp ;; ;; db3.lisp #| Database file structure The structure of a dBASE III database file is composed of a header and data records. The layout is given below. dBASE III DATABASE FILE HEADER: +---------+-------------------+---------------------------------+ | BYTE | CONTENTS | MEANING | +---------+-------------------+---------------------------------+ | 0 | 1 byte | dBASE III version number | | | | (03H without a .DBT file) | | | | (83H with a .DBT file) | +---------+-------------------+---------------------------------+ | 1-3 | 3 bytes | date of last update | | | | (YY MM DD) in binary format | +---------+-------------------+---------------------------------+ | 4-7 | 32 bit number | number of records in data file | +---------+-------------------+---------------------------------+ | 8-9 | 16 bit number | length of header structure | +---------+-------------------+---------------------------------+ | 10-11 | 16 bit number | length of the record | +---------+-------------------+---------------------------------+ | 12-31 | 20 bytes | reserved bytes (version 1.00) | +---------+-------------------+---------------------------------+ | 32-n | 32 bytes each | field descriptor array | | | | (see below) | --+ +---------+-------------------+---------------------------------+ | | n+1 | 1 byte | 0DH as the field terminator | | +---------+-------------------+---------------------------------+ | | | A FIELD DESCRIPTOR: <------------------------------------------+ +---------+-------------------+---------------------------------+ | BYTE | CONTENTS | MEANING | +---------+-------------------+---------------------------------+ | 0-10 | 11 bytes | field name in ASCII zero-filled | +---------+-------------------+---------------------------------+ | 11 | 1 byte | field type in ASCII | | | | (C N L D or M) | +---------+-------------------+---------------------------------+ | 12-15 | 32 bit number | field data address | | | | (address is set in memory) | +---------+-------------------+---------------------------------+ | 16 | 1 byte | field length in binary | +---------+-------------------+---------------------------------+ | 17 | 1 byte | field decimal count in binary | +---------+-------------------+---------------------------------+ | 18-31 | 14 bytes | reserved bytes (version 1.00) | +---------+-------------------+---------------------------------+ The data records are layed out as follows: 1. Data records are preceeded by one byte that is a space (20H) if the record is not deleted and an asterisk (2AH) if it is deleted. 2. Data fields are packed into records with no field separators or record terminators. 3. Data types are stored in ASCII format as follows: DATA TYPE DATA RECORD STORAGE --------- -------------------------------------------- Character (ASCII characters) Numeric - . 0 1 2 3 4 5 6 7 8 9 Logical ? Y y N n T t F f (? when not initialized) Memo (10 digits representing a .DBT block number) Date (8 digits in YYYYMMDD format, such as 19840704 for July 4, 1984) |# (in-package :db3) (defparameter *external-format* :ascii "External format of the DBF file Character data") ;;; reading binary stuff (defun read-uint32 (stream) (loop repeat 4 for offset from 0 by 8 for value = (read-byte stream) then (logior (ash (read-byte stream) offset) value) finally (return value))) (defun read-uint16 (stream) (loop repeat 2 for offset from 0 by 8 for value = (read-byte stream) then (logior (ash (read-byte stream) offset) value) finally (return value))) ;;; objects (defclass db3 () ((version-number :accessor version-number) (last-update :accessor last-update) (record-count :accessor record-count) (header-length :accessor header-length) (record-length :accessor record-length) (fields :accessor fields))) (defclass db3-field () ((name :accessor field-name) (type :accessor field-type) (data-address :accessor data-address) (field-length :accessor field-length) (field-count :accessor field-count))) (defun asciiz->string (array) (let* ((string-length (or (position 0 array) (length array))) (string (make-string string-length))) (loop for i below string-length do (setf (schar string i) (code-char (aref array i)))) string)) (defun ascii->string (array) (cond ((eq :ascii *external-format*) (map 'string #'code-char array)) (t #+sbcl (sb-ext:octets-to-string array :external-format *external-format*) #+ccl (ccl:decode-string-from-octets array :external-format *external-format*)))) (defun load-field-descriptor (stream) (let ((field (make-instance 'db3-field)) (name (make-array 11 :element-type '(unsigned-byte 8)))) (read-sequence name stream) (setf (field-name field) (asciiz->string name) (field-type field) (code-char (read-byte stream)) (data-address field) (read-uint32 stream) (field-length field) (read-byte stream) (field-count field) (read-byte stream)) (loop repeat 14 do (read-byte stream)) field)) (defmethod field-count ((db3 db3)) (1- (/ (1- (header-length db3)) 32))) (defmethod load-header ((db3 db3) stream) (let ((version (read-byte stream))) (unless (= version #x03) (error "Can't handle this file")) (let ((year (read-byte stream)) (month (read-byte stream)) (day (read-byte stream))) (setf (version-number db3) version (last-update db3) (list year month day) (record-count db3) (read-uint32 stream) (header-length db3) (read-uint16 stream) (record-length db3) (read-uint16 stream)) (file-position stream 32) (setf (fields db3) (loop repeat (field-count db3) collect (load-field-descriptor stream))) (assert (= (read-byte stream) #x0D)) db3))) (defmethod convert-field (type data) (ascii->string data)) (defmethod convert-field ((type (eql #\C)) data) (ascii->string data)) (defmethod load-field (type length stream) (let ((field (make-array length :element-type '(unsigned-byte 8)))) (read-sequence field stream) (convert-field type field))) (defmethod load-record ((db3 db3) stream) (read-byte stream) (loop with record = (make-array (field-count db3)) for i below (field-count db3) for field in (fields db3) do (setf (svref record i) (load-field (field-type field) (field-length field) stream)) finally (return record))) (defun write-record (record stream) (loop for field across record do (write-char #\" stream) (write-string field stream) (write-string "\"," stream)) (terpri stream)) (defun dump-db3 (input output) (with-open-file (stream input :direction :input :element-type '(unsigned-byte 8)) (with-open-file (ostream output :direction :output :element-type 'character) (let ((db3 (make-instance 'db3))) (load-header db3 stream) (loop repeat (record-count db3) do (write-record (load-record db3 stream) ostream)) db3)))) (defun sample-db3 (input ostream &key (sample-size 10)) (with-open-file (stream input :direction :input :element-type '(unsigned-byte 8)) (let ((db3 (make-instance 'db3))) (load-header db3 stream) (loop :repeat sample-size :do (format ostream "~s~%" (load-record db3 stream))) db3))) cl-db3-20150302-git/package.lisp000066400000000000000000000003421247120266600160660ustar00rootroot00000000000000;;;; package.lisp (defpackage #:db3 (:use #:cl) (:export #:*external-format* #:db3 ; the main class #:load-header #:record-count #:load-record #:write-record #:dump-db3 #:sample-db3))