Information for converting PDB files

You have to define a struct PDBConv for every specialized PDB file. It contains functions that know how to pack and unpack three things: the application info, the sort area, and a record. It also contains documents (char **) that explain to somebody reading the ASCII what the particular conventions are. It is particularly important to document packing, because many of the fields written out by the unpacker are probably optional.

We recognize a file by its type and creator, so the converter must contain those too, to indicate what it applies to.

Pack methods will be called twice: once with null argument, and once without. In both cases they must return the number of bytes that would be (or are) written.

<header>= (U->) [D->]
#include "pdbio.h"
struct pdb;                     /* pointer to PDB database */

typedef struct PDBConv {
  char *application;            /* identifying noun for comment */
  char type[5];                 /* includes null terminator */
  char creator[5];              /* includes null terminator */
  struct infoconv {
    char **docs;
    void (*unpack)(FILE *lua, struct pdb *db, Text image);
                                /* read image from pdb file, write Lua code */
    int (*pack)(FILE *pdb, int appinfo);
                                /* read info from Lua value Database, write if pdb 
                                   not null, and return number of bytes written.
                                   appinfo is nonzero for application info,
                                   zero for sort info */
  } appInfo, sortInfo;
  struct recordconv {
    char **docs;
    void (*unpack)(FILE *lua, struct pdb *db, int recnum);
                                /* read image db record area, write Lua code */
    int (*pack)(FILE *pdb, int recnum);
                                /* read info from Lua value Database,
                                   write if pdb not null,
                                   and return number of bytes written */
  } record;
  char **trailers;              /* useful functions or whatever */
} *PDBConverter;
Defines PDBConverter (links are to index).

This module includes support for packing and unpacking dates.

<header>+= (U->) [<-D->]
#include <time.h>
#include "pdblua.h"
extern void unpack_date(FILE *out, time_t secs);
extern time_t pack_date(Ref o);
extern char **date_docs;

It also provides a generic converter, which uses these generic packing and unpacking functions.

<header>+= (U->) [<-D]
extern PDBConverter generic_convert;
extern void generic_unpackInfo  (FILE *lua, struct pdb *db, Text image);
extern void generic_unpackRecord(FILE *lua, struct pdb *db, int recnum);
extern int generic_packInfo  (FILE *pdb, int appinfo);
extern int generic_packRecord(FILE *pdb, int recnum);

Implementation

<*>=
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "pdbrep.h"
#include "pdblua.h"
<header>
<functions>

Lua doesn't have enough precision to represent a 32-bit time as a number, so we make it a string.

<functions>= (<-U) [D->]
static char *weekdayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
void unpack_date(FILE *out, time_t secs) {
  struct tm* t = localtime(&secs);
  fprintf(out, "{ month = %d, day = %d, year = %d, weekday = \"%s\",\n",
          t->tm_mon+1, t->tm_mday, 1900+t->tm_year, weekdayNames[t->tm_wday]);
  fprintf(out, "      hour = %02d, min = %02d, sec = %02d,\n",
          t->tm_hour, t->tm_min, t->tm_sec);
  fprintf(out, "      unixtime = \"%lu\" }", secs);
}
Defines unpack_date, weekdayNames (links are to index).

<functions>+= (<-U) [<-D->]
#define FIELD(N,OPT) \
  (lua_pushobject(deref(o)), lua_pushstring(N), \
   intoptf(lua_gettable(), OPT, "Field " N " of date"))

extern time_t pack_date(Ref o) {
  struct tm t, now;
  time_t secs;
  lua_Object unixtime;
  lua_beginblock();
  if (!lua_istable(deref(o))) {
    fprintf(stderr, "Date is not a table; printing type and value\n");
    lua_dostring("writeto(_STDERR)");
    lua_pushobject(deref(o));
    lua_call("type");
    lua_pushobject(lua_getresult(1));
    lua_call("print");
    lua_pushobject(deref(o));
    lua_call("print");
    exit(1);
  }
  lua_pushobject(deref(o));
  lua_pushstring("unixtime");
  unixtime = lua_gettable();
  if (lua_isstring(unixtime)) {
    secs = (time_t) strtoul(lua_getstring(unixtime), NULL, 10);
    lua_endblock();
    return secs;
  } else {
    secs = time(NULL);
    now = *localtime(&secs);
    memset(&t, 0, sizeof(t));
  
    t.tm_year = FIELD("year",  now.tm_year+1900) - 1900;
    t.tm_mon  = FIELD("month", now.tm_mon);
    t.tm_mday = FIELD("dat",   now.tm_mday);
    t.tm_hour = FIELD("hour",  now.tm_hour);
    t.tm_min = FIELD("min",  now.tm_min);
    t.tm_sec = FIELD("sec",  now.tm_sec);
    lua_endblock();
    return mktime(&t);
  }
}
Defines FIELD (links are to index).

<functions>+= (<-U) [<-D->]
void generic_unpackInfo(FILE *fp, struct pdb *db, Text t) {
  char *p, *s, *limit, c;
  if (!t.chars || t.len == 0)
    fprintf(fp, "nil");
  else {
    fprintf(fp, "{ ");
    p = t.chars; 
    limit = p + t.len;

    do {
      for (s = p; *s && s < limit; s++);
      lua_beginblock();
      c = *s; *s = 0;
      fprintf(fp, "%s%s", p > t.chars ? ", " : "", QUOTE(p));
      *s = c;
      p = s + 1;
      lua_endblock();
    } while (p < limit);    
 
    fprintf(fp, " }");
  }       
}
void generic_unpackRecord(FILE *fp, struct pdb *db, int recnum) {
  generic_unpackInfo(fp, db, db->records[recnum].data);
}
Defines generic_unpackInfo, generic_unpackRecord (links are to index).

<functions>+= (<-U) [<-D->]
static int generic_pack_object  (FILE *pdb, Ref info, char *exp) {
  lua_Object s;
  int n = 0;
  int i;
  if (lua_isnil(deref(info))) {
    return 0;
  } else if (!lua_istable(deref(info))) {
    fprintf(stderr, "Generic info %s is not a table\n", exp);
    exit(1);
  }
  for (i = 1; ; i++) {
    lua_beginblock();
    lua_pushobject(deref(info));
    lua_pushnumber((double)i);
    s = lua_gettable();
    if (lua_isnil(s)) { lua_endblock(); break; }
    if (!lua_isstring(s)) {
      fprintf(stderr, "Element of generic info %s is not a string\n", exp);
      exit(1);
    }
    if (pdb) { fprintf(pdb, "%s", lua_getstring(s)); putc('\0', pdb); }
    n += strlen(lua_getstring(s)) + 1;
    lua_endblock();
  }
  my_unref(info);
  return n;
}
Defines generic_pack_object (links are to index).

<functions>+= (<-U) [<-D->]
int generic_packInfo (FILE *pdb, int appinfo) {
  int n;
  lua_beginblock();
  n = appinfo
    ? generic_pack_object(pdb, objectval("Database.appInfo"),  "Database.appInfo")
    : generic_pack_object(pdb, objectval("Database.sortInfo"), "Database.sortInfo");
  lua_endblock();
  return n;
}
int generic_packRecord(FILE *pdb, int recnum) {
  static char buf[80];
  int n;
  sprintf(buf, "return Database.records[%d].data", recnum+1);
  lua_beginblock();
  n = generic_pack_object(pdb, myref(objectvalf(buf, buf)), buf + 7);
  lua_endblock();
  return n;
}
Defines generic_packInfo, generic_packRecord (links are to index).

<functions>+= (<-U) [<-D]
static char *gdata[] = {
  "This is generic data represented as a list of Lua strings.",
  "A Lua string can hold any value except zero, but every Lua",
  "string ends with zero, so by using a list of them we can",
  "show arbitrary binary data.  We can tell whether the final",
  "zero is included because we know the length of the data.",
  NULL
};

struct PDBConv generic_converter = {
  (char *)0,
  "\0\0\0\0\0", "\0\0\0\0\0",
  { gdata, generic_unpackInfo, generic_packInfo },
  { gdata, generic_unpackInfo, generic_packInfo },
  { gdata, generic_unpackRecord, generic_packRecord },
  (char **)0
};
PDBConverter generic_convert = &generic_converter;

Defines gdata, generic_convert, generic_converter (links are to index).