############################################################################
#
# File: ddfread.icn
#
# Subject: Procedures for reading ISO 8211 DDF files
#
# Author: Gregg M. Townsend
#
# Date: June 26, 2000
#
############################################################################
#
# This file is in the public domain.
#
############################################################################
#
# These procedures read DDF files ("Data Descriptive Files",
# ISO standard 8211) such as those specified by the US Geological
# Survey's "Spatial Data Transfer Standard" for digital maps.
# ISO8211 files from other sources may contain additional data
# encodings not recognized by these procedures.
#
# ddfopen(filename) opens a file and returns a handle.
# ddfdda(handle) returns a list of header records.
# ddfread(handle) reads the next data record.
# ddfclose(handle) closes the file.
#
############################################################################
#
# ddfopen(filename) opens a DDF file, decodes the header, and
# returns an opaque handle for use with subsequent calls. It
# fails if any problems are encountered. Instead of a filename,
# an already-open file can be supplied.
#
############################################################################
#
# ddfdda(handle) returns a list of records containing data
# from the Data Descriptive Area (DDA) of the file header.
# Each record contains the following fields:
#
# tag DDR entry tag
# control field control data
# name field name
# labels list of field labels
# format data format
#
# The records may also contain other fields used internally.
#
############################################################################
#
# ddfread(handle) reads the next data record from the file.
# It returns a list of lists, with each sublist containing
# a tag name followed by the associated data values, already
# decoded according to the specification given in the header.
#
############################################################################
#
# ddfclose(handle) closes a DDF file.
#
############################################################################
$define RecSep "\x1E" # ASCII Record Separator
$define UnitSep "\x1F" # ASCII Unit Separator
$define EitherSep '\x1E\x1F' # either separator, as cset
$define LabelSep "!" # label separator
$define AnySep '!\x1E\x1F' # any separator, as cset
record ddf_info( # basic DDF file handle
file, # underlying file
header, # last header
dlist, # DDA list (of ddf_dde records)
dtable # DDA table (indexed by tag)
)
record ddf_header( # DDF header information
hcode, # header code (R if to reuse)
dlen, # data length
ddata, # dictionary data (as a string)
tsize, # size of tag field in dictionary
lsize, # size of length field
psize, # size of position field
s # header string
)
record ddf_dde( # data description entry
tag, # record tag
control, # field control
name, # field name
rep, # non-null if labels repeat to end of record
labels, # list of labels
format, # format
dlist # decoder list
)
record ddf_decoder( # field decoder record
proc, # decoding procedure
arg # decoder argument
)
######################### PUBLIC PROCEDURES #########################
# ddfopen(filename) -- open DDF file for input
#
# Opens a DDF file, decodes the header, and returns an opaque handle h
# for use with ddfread(h). Fails if any problems are found.
procedure ddfopen(fname) #: open DDF file
local f, h, p, l, t, e
if type(fname) == "file" then
f := fname
else
f := open(fname, "ru") | fail
h := ddf_rhdr(f) | fail
p := ddf_rdata(f, h) | fail
l := dda_list(p) | fail
t := table()
every e := !l do
t[e.tag] := e
return ddf_info(f, h, l, t)
end
# ddfdda(handle) -- return list of DDAs
#
# Returns a list of Data Descriptive Area records containing the
# following fields:
#
# tag DDR entry tag
# control field control data
# name field name
# labels list of field labels
# format data format
#
# (There may be other fields present for internal use.)
procedure ddfdda(handle)
return handle.dlist
end
# ddfread(handle) -- read DDF record
#
# Reads the next record using a handle returned by ddfopen().
# Returns a list of lists, each sublist consisting of a
# tag name followed by the associated data values
procedure ddfread(handle) #: read DDF record
local h, p, dlist, code, data, drec, sublist, e
h := handle.header
if h.hcode ~== "R" then
h := handle.header := ddf_rhdr(handle.file) | fail
p := ddf_rdata(handle.file, h) | fail
dlist := list()
while code := get(p) do {
data := get(p)
drec := \handle.dtable[code] | next # ignore unregistered code
put(dlist, sublist := [code])
data ? {
while not pos(0) do {
every e := !drec.dlist do
every put(sublist, e.proc(e.arg))
if /drec.rep | (pos(-1) & =RecSep) then
break
}
}
}
return dlist
end
# ddfclose(handle) -- close DDF file
procedure ddfclose(handle) #: close DDF file
close(\handle.file)
every !handle := &null
return
end
######################### INTERNAL PROCEDURES #########################
# ddf_rhdr(f) -- read DDF header record
procedure ddf_rhdr(f)
local s, t, tlen, hcode, off, nl, np, nx, nt, ddata
s := reads(f, 24) | fail
*s = 24 | fail
s ? {
tlen := integer(move(5)) | fail
move(1)
hcode := move(1)
move(5)
off := integer(move(5)) | fail
move(3) | fail
nl := integer(move(1)) | fail
np := integer(move(1)) | fail
nx := move(1) | fail
nt := integer(move(1)) | fail
}
ddata := reads(f, off - 24) | fail
*ddata = off - 24 | fail
return ddf_header(hcode, tlen - off, ddata, nt, nl, np, s)
end
# ddf_rdata(f, h) -- read data, returning code/value pairs in list
procedure ddf_rdata(f, h)
local tag, len, posn, data, a, d
d := reads(f, h.dlen) | fail
if *d < h.dlen then fail
a := list()
h.ddata ? while not pos(0) do {
if =RecSep then break
tag := move(h.tsize) | fail
len := move(h.lsize) | fail
posn := move(h.psize) | fail
data := d[posn + 1 +: len] | fail
put(a, tag, data)
}
return a
end
# dda_list(pairs) -- build DDA list from tag/data pairs
procedure dda_list(p)
local l, labels, tag, spec, control, name, format, d, rep
l := list()
while tag := get(p) do {
labels := list()
spec := get(p) | fail
spec ? {
control := move(6) | fail
name := tab(upto(EitherSep) | 0)
move(1)
rep := ="*"
while put(labels, tab(upto(AnySep))) do {
if =LabelSep then next
move(1)
break
}
format := tab(upto(EitherSep) | 0)
move(1)
pos(0) | fail
}
d := ddf_dtree(format) | fail
put(l, ddf_dde(tag, control, name, rep, labels, format, d))
}
return l
end
# ddf_dtree(format) -- return tree of decoders for format
#
# keeps a cache to remember & share decoder lists for common formats
procedure ddf_dtree(format)
static dcache
initial {
dcache := table()
dcache[""] := [ddf_decoder(ddf_str, EitherSep)]
}
/dcache[format] := ddf_fcrack(format[2:-1])
return dcache[format]
end
# ddf_fcrack(s) -- crack format string
procedure ddf_fcrack(s)
local dlist, n, d
dlist := list()
s ? while not pos(0) do {
if (any(&digits)) then
n := tab(many(&digits))
else
n := 1
d := &null
d := case move(1) of {
",": next
"A": ddf_oneof(ddf_str, ddf_strn)
"B": ddf_oneof(&null, ddf_binn, 8)
"I": ddf_oneof(ddf_int, ddf_intn)
"R": ddf_oneof(ddf_real, ddf_realn)
"(": ddf_decoder(ddf_repeat, ddf_fcrack(tab(bal(')')), move(1)))
}
if /d then fail
every 1 to n do
put(dlist, d)
}
return dlist
end
# ddf_oneof(tabproc, moveproc, quantum) -- select one of two procs
procedure ddf_oneof(tabproc, moveproc, quantum)
local d, n
if not ="(" then
return ddf_decoder(tabproc, EitherSep)
if any(&digits) then {
/quantum := 1
n := integer(tab(many(&digits)))
n % quantum = 0 | fail
d := ddf_decoder(moveproc, n / quantum)
}
else {
d := ddf_decoder(\tabproc, move(1) ++ EitherSep) | fail
}
=")" | fail
return d
end
######################### DECODING PROCEDURES #########################
procedure ddf_str(cs) # delimited string
return 1(tab(upto(cs)), move(1))
end
procedure ddf_strn(n) # string of n characters
return move(n)
end
procedure ddf_int(cs) # delimited integer
local s
s := tab(upto(cs))
move(1)
return integer(s) | 0
end
procedure ddf_intn(n) # integer of n digits
local s
s := move(n)
return integer(s) | 0
end
procedure ddf_real(cs) # delimited real
local s
s := tab(upto(cs))
move(1)
return real(s) | 0.0
end
procedure ddf_realn(n) # real of n digits
local s
s := move(n)
return real(s) | 0.0
end
procedure ddf_binn(n) # binary value of n bytes
local v, c
v := c := ord(move(1))
every 2 to n do
v := 256 * v + ord(move(1))
if c < 128 then # if sign bit unset in first byte
return v
else
return v - ishift(1, 8 * n)
end
procedure ddf_repeat(lst) # repeat sublist to EOR
local e
repeat {
every e := !lst do {
if (=RecSep | &null) & pos(0) then
fail
else
suspend e.proc(e.arg)
}
}
end
This page produced by UniDoc on 2021/04/15 @ 23:59:44.