Input and output for Pilot Data Base conversion

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.

Interface

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))
Defines chars_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))
Defines get_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;

Implementation

<*>=
#include <string.h>
#include <assert.h>
<header>
<functions>

Standard creation.

<functions>= (<-U) [D->]
Text text(char *chars, unsigned len) {
  Text t;
  t.chars = chars;
  t.len = len;
  t.p = (unsigned char *)chars;
  return t;
}
Defines text (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);
}
Defines readfile (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);
}
Defines io_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);
}



Defines put_chars (links are to index).