Reading a Pilot database pretty much requires random access, so we
read the entire thing into a Text
and take it from there.
Writing it, on the other hand, can be done sequentially (provided
we're willing to make two passes), so we just
use an ordinary file.
A Text
is a string possibly containing zeroes, plus a ``current
pointer'' that marks a position to read from.
It's often useful to check how many characters have been read or
remain to be read.
<header>= (U->) [D->] typedef struct text { char *chars; /* pointer to entire text */ unsigned len; /* number of characters in text */ unsigned char *p; /* current pointer (changed by i/o) */ } Text; #define chars_used(T) ((char *)(T)->p - (T)->chars) #define chars_remaining(T) ((T)->len - chars_used(T))
Defineschars_remaining
,chars_used
,Text
(links are to index).
You can build a Text
by reading in an entire file, or by giving a
buffer and length.
<header>+= (U->) [<-D->] #include <stdio.h> extern Text readfile(FILE *fp); /* read all chars left in file */ extern Text text(char *chars, unsigned len); /* create a new one */
I/O functions have to modify the current pointer, so they work on a
pointer to a text. All of them arrange to call the iofail
macro
if the Text
is exhausted.
iofail
is a macro so it can give the source location of the
offending read.
<header>+= (U->) [<-D->] #include <stdlib.h> #define iofail(T,N) \ ((void)(chars_remaining(T) >= (N) ? 0 : \ (fprintf(stderr, "%s:%d: exhausted character buffer\n",__FILE__, __LINE__), \ abort(), 0))) #define get_byte(T) (iofail(T,1), *(T)->p++) #define get_short(T) (io_btmp = get_byte(T), io_btmp << 8 | get_byte(T)) #define get_long(T) (io_stmp = get_short(T), io_stmp << 16 | get_short(T)) #define get_chars(D,S,N) (iofail(S,N),get_charsf(D,S,N)) #define get_array(A,T) get_chars(A,T,sizeof(A)) extern void get_charsf(void *dst, Text *src, int n); #define put_byte(F, N) (putc(N, F) != EOF ? (int)(unsigned char)(N) : (assert(0), EOF)) extern void put_short(FILE *, unsigned short); extern void put_long (FILE *, unsigned long); extern void put_chars(FILE *dst, void *src, int n); #define put_block(DST, SRC) put_chars(DST, SRC, sizeof(SRC))
Definesget_array
,get_byte
,get_chars
,get_long
,get_short
,iofail
,put_block
,put_byte
(links are to index).
The macros use the byte order of the Pilot (big-endian), regardless of the byte order of the host machine. These two temporaries are used to simplify ordering:
<header>+= (U->) [<-D] /* temps for macros */ extern unsigned char io_btmp; extern unsigned short io_stmp;
<*>= #include <string.h> #include <assert.h> <header> <functions>
<functions>= (<-U) [D->] Text text(char *chars, unsigned len) { Text t; t.chars = chars; t.len = len; t.p = (unsigned char *)chars; return t; }
Definestext
(links are to index).
Read an entire file. Note we always leave one unused character at the
end of a buffer, in case it's ever necessary to plop a zero there
using the pushchar
trick.
<functions>+= (<-U) [<-D->]
Text readfile(FILE *fp) {
int c;
int n = 4096;
char *buf = malloc(n), *p;
assert(buf);
for (p = buf; (c = getc(fp)) != EOF; ) {
if (p - buf == n - 1) {<double buf
>}
*p++ = c;
}
if (!feof(fp)) {
perror("Error reading reading file");
exit(1);
}
return text(buf, p-buf);
}
Definesreadfile
(links are to index).
<double buf
>= (<-U)
{ int i = p - buf;
buf = realloc(buf, n = 2 * n);
assert(buf);
p = buf + i;
}
The Pilot is big-endian.
This code will work on all machines.
These are functions, not macros, to guarantee not to evaluate n
twice.
<functions>+= (<-U) [<-D->] unsigned char io_btmp; unsigned short io_stmp; void put_short(FILE *t, unsigned short n) { put_byte(t, (n >> 8) & 0xff); put_byte(t, n & 0xff); } void put_long(FILE *t, unsigned long n) { put_short(t, (n >> 16) & 0xffff); put_short(t, n & 0xffff); }
Definesio_btmp
,io_stmp
,put_long
,put_short
(links are to index).
<functions>+= (<-U) [<-D] extern void get_charsf(void *dst, Text *src, int n) { assert((char *)src->p + n <= src->chars + src->len); memcpy(dst, src->p, n); src->p += n; } void put_chars(FILE *dst, void *src, int n) { int rc =fwrite(src, 1, n, dst); if (ferror(dst)) { perror("Error occurred writing binary file"); exit(1); } assert(rc == n); }
Definesput_chars
(links are to index).