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).