PDB converstion for Handy Shopper

first of all here are the answers (source code pointers): -- grep for AppInfo. definition is in globals.h, "ThisAppInfoType" -- no automatic ("live") sorting in 1.01, it's just a menu command that does a static sort (v2 will have optional live sorting) -- grep for ItemPtr. definition is in globals.h, "Item"

<*>=
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>


#include "pushchar.h"
#include "pdblua.h"
#include "pdbio.h"
#include "pdbrep.h"
#include "pdbconv.h"

<functions>

P11. An application info structure is intended to be stored in the database's app info block and to contain data relevant to the database. For now we store just category information. Category information is always placed in the beginning where the category.c code expects it.

<functions>= (<-U) [D->]
#define MAX_STORES 15
#define MAXSTNAME 32

struct HSAppInfo {
  struct PDBCategoryAppInfo categories;
  char padding[1];
  unsigned short cStores;       /* how many stores */
  unsigned short bitStores;     /* mask of used bits (or of all stores?) */
  unsigned short rgbitStores[MAX_STORES]; /*  if store exists, its bitmask */
  unsigned short rgcItemsInStore[MAX_STORES+1]; /* [0] is "All Stores" (total) count */
  char rgszStoreNames[MAX_STORES][MAXSTNAME+1];   
  char padding2[1];
};
Defines MAXSTNAME, MAX_STORES (links are to index).

<functions>+= (<-U) [<-D->]
static struct HSAppInfo readhsinfo(Text*);
static void dumphsi(FILE *out, struct pdb *db, Text txt) {
  Text *t = &txt;
  struct HSAppInfo info = readhsinfo(t);
  int i, last;

  for (i = 0; i < info.cStores; i++)
    if (info.rgbitStores[i])
      last = i;
 
  fprintf(out, "\n    {");
  fprintf(out, "\n      categories = ");
  unpack_categories(out, &info.categories, 1);
  fprintf(out,",\n      totalItems = %d", info.rgcItemsInStore[0]);
  fprintf(out,",\n      stores = {");
  for (i = 0; i < info.cStores; i++)
    if (info.rgbitStores[i])
      fprintf(out, "\n        { name = %s, mask = %d, items = %d }%s",
              QUOTE(info.rgszStoreNames[i]),
              info.rgbitStores[i], info.rgcItemsInStore[i+1],
              i < last ? "," : "");
  fprintf(out,"\n      },");
  fprintf(out,"\n      bitStores = %d,", info.bitStores);
  fprintf(out,"\n      padding = { %d, %d }", info.padding[0], info.padding2[0]);
  fprintf(out,"\n    }");
}
Defines dumphsi (links are to index).

<functions>+= (<-U) [<-D->]
static int writehsinfo(FILE *fp, struct HSAppInfo *info) {
  int i, n;
  long pos = fp ? ftell(fp) : 0;

  n = catwrite(fp, &info->categories);
  n +=  1 + 2 + 2 + MAX_STORES*2 + (MAX_STORES+1)*2 + sizeof(info->rgszStoreNames) + 1;
  if (fp) {
    put_chars(fp, info->padding, 1);
    put_short(fp, info->cStores);
    put_short(fp, info->bitStores);
    for (i = 0; i < MAX_STORES; i++)
      put_short(fp, info->rgbitStores[i]);
    for (i = 0; i < MAX_STORES+1; i++)
      put_short(fp, info->rgcItemsInStore[i]);
    put_chars(fp, info->rgszStoreNames, sizeof(info->rgszStoreNames));
    put_chars(fp, info->padding2, 1);
    assert(ftell(fp) - pos == n);
  }
  return n;
}
Defines writehsinfo (links are to index).

<functions>+= (<-U) [<-D->]
static struct HSAppInfo readhsinfo(Text *t) {
  struct HSAppInfo info;
  int i;
  unsigned char *curp = t->p;

  info.categories = catread(t);
  get_chars(info.padding, t, 1);

  info.cStores = get_short(t);
  assert(info.cStores <= MAX_STORES);
  info.bitStores = get_short(t);
  for (i = 0; i < MAX_STORES; i++)
    info.rgbitStores[i] = get_short(t);
  for (i = 0; i < MAX_STORES+1; i++)
    info.rgcItemsInStore[i] = get_short(t);
  get_chars(info.rgszStoreNames, t, sizeof(info.rgszStoreNames));
  get_chars(info.padding2, t, 1);
  if (chars_remaining(t) > 0)
    fprintf(stderr, "Warning: %d unused bytes of HandyShopper info\n",
            chars_remaining(t));

  t->p = (unsigned char *) curp;
  return info;
}
Defines readhsinfo (links are to index).

<functions>+= (<-U) [<-D->]
static int pack(FILE *pdb, int appinfo) {
  struct HSAppInfo info;
  memset(&info, 0, sizeof(info));
  if (pdb) {
    int i;
    <set the fields of the info structure>
  }
  return writehsinfo(pdb, &info);
}
Defines pack (links are to index).

<write the info structure>=
assert(0);
<set the fields of the info structure>= (<-U)
info.categories = pack_categories();
if (lua_dostring("info = Database.appInfo")) assert(0);
dbcheck("istable(info)", "application info must be a table");
info.padding[0]  = intopt("istable(info.padding) and info.padding[1]", 0);
info.padding2[0] = intopt("istable(info.padding) and info.padding[2]", 0);
info.rgcItemsInStore[0] = intval("info.totalItems");
dbcheck("istable(info.stores)", "application info must have stores list");
info.cStores = intval("listlength(info.stores)");
info.bitStores = intval("info.bitStores");
for (i = 0; i < info.cStores; i++) {
  lua_beginblock();
  <export i to Lua, offset by 1>
  info.rgbitStores[i] = intval("istable(info.stores[i]) and info.stores[i].mask");
  setstring(info.rgszStoreNames[i],
              "istable(info.stores[i]) and info.stores[i].name or info.stores[i]",
              MAXSTNAME);
  info.rgcItemsInStore[i+1] =
    intval("istable(info.stores[i]) and info.stores[i].items");
  lua_endblock();
}     

<export i to Lua, offset by 1>= (<-U U->)
(lua_pushnumber((double)(i+1)), lua_setglobal("i"));

<functions>+= (<-U) [<-D->]
typedef struct HSRecord {
  unsigned char flags;          /* ?? */
  unsigned char namesize;       /* length of descriptoin excluding null terminator */
  unsigned short stores;        /* 16 bits mapping to stores */
  unsigned short quantity; 
  unsigned long price;
  unsigned char aisle;
  char *name;                   /* may be longer */
} Item, *ItemPtr;

Defines Item, ItemPtr (links are to index).

<functions>+= (<-U) [<-D->]
static struct HSRecord hsread(Text *t, int recnum) {
  struct HSRecord rec;
  unsigned char *p = t->p;
  rec.flags = get_byte(t);
  rec.namesize = get_byte(t);
  rec.stores = get_short(t);
  rec.quantity = get_short(t);
  rec.price = get_long(t);
  rec.aisle = get_byte(t);
  rec.name = malloc(rec.namesize+1);
  assert(rec.name);
  get_chars(rec.name, t, rec.namesize+1);
  assert(rec.name[rec.namesize] == 0);

  if (chars_remaining(t) > 0)
    fprintf(stderr, "Warning: %d unused bytes in record %d\n",
            chars_remaining(t), recnum);

  t->p = p;
  return rec;
}
Defines hsread (links are to index).

<functions>+= (<-U) [<-D->]
static int hswrite(FILE *fp, struct HSRecord *rec) {
  int n = 1+1+2+2+4+1+rec->namesize+1;

n++;  /* paranoia, to pass binary test */
  if (fp) {
    long pos = ftell(fp);
    put_byte(fp, rec->flags);
    put_byte(fp, rec->namesize);
    put_short(fp, rec->stores);
    put_short(fp, rec->quantity);
    put_long(fp, rec->price);
    put_byte(fp, rec->aisle);
    put_chars(fp, rec->name, rec->namesize+1);
putc(0, fp);  /* paranoia, to pass binary test */
    assert(ftell(fp) - pos == n);
  }
  return n;
}

Defines hswrite (links are to index).

<functions>+= (<-U) [<-D->]
#define flagNeed     0x01
#define flagCoupon   0x02
#define flagPrivate  0x80 /* private (home-brewed so we can keep store bits in sync) */

static int packr(FILE *pdb, int i) {
  struct HSRecord rec;
  <export i to Lua, offset by 1>
  if (lua_dostring(
        "$debug\n"
        "thisrecord = Database.records[i].data\n"
        "if not storemasks then\n"
        "  storemasks = { }\n"
        "  j = 1\n"
        "  while Database.appInfo.stores[j] do\n"
        "    storemasks[Database.appInfo.stores[j].name] = "
        "      Database.appInfo.stores[j].mask\n"
        "    j = j + 1\n"
        "  end\n"
        "end")) 
    assert(0);
  dbcheck("istable(thisrecord)", "record data is not a table");
  rec.name = stringval("thisrecord.name");
  rec.namesize = strlen(rec.name);
  rec.flags = 0;
  if (bitval("thisrecord.need"))    rec.flags |= flagNeed;
  if (bitval("thisrecord.coupon"))  rec.flags |= flagCoupon;
  if (bitval("thisrecord.private")) rec.flags |= flagPrivate;
  if (lua_dostring(
        "$debug\n"
        "j = istable(thisrecord.stores) or error('thisrecord.stores not a list')\n"
        "thisrecord.storemask = 0\n"
        "j = 1\n"
        "while thisrecord.stores[j] do\n"
        "  thisrecord.storemask = \n"
        "    thisrecord.storemask + storemasks[thisrecord.stores[j]]\n"
        "  j = j + 1\n"
        "end\n"
     ))
    assert(0);
  rec.stores = intval("thisrecord.storemask");
  rec.quantity = intopt("thisrecord.quantity", 0);
  rec.price = intopt("thisrecord.price and (100 * thisrecord.price)", 0);
  rec.aisle = intopt("thisrecord.aisle", 1);
  return hswrite(pdb, &rec);
}
Defines flagCoupon, flagNeed, flagPrivate, packr (links are to index).

<functions>+= (<-U) [<-D->]
static void dumphsr(FILE *out, struct pdb *db, int i);
static struct HSRecord hsread(Text *t, int recnum);

#define BIT(x) ((x) ? "1" : "nil")
static void dumphsr(FILE *out, struct pdb *db, int recnum) {
  char *p = db->info.appInfo.chars;
  struct HSAppInfo info = readhsinfo(&db->info.appInfo);
  struct HSRecord item;
  int last, i;

  p = db->records[recnum].data.chars;
  item = hsread(&db->records[recnum].data, recnum);

  fprintf(out, "{");
  fprintf(out, "\n        name = %s,", QUOTE(item.name));
  fprintf(out, "\n        quantity = %d, price = %4.02f, aisle = %d,",
          item.quantity, (double)item.price / 100.0, item.aisle);
  fprintf(out, "\n        need = %s, coupon = %s, private = %s,",
          BIT(item.flags & flagNeed), BIT(item.flags & flagCoupon),
          BIT(item.flags & flagPrivate));
  fprintf(out, "\n        stores = { ");
  for (i = 0; i < info.cStores; i++)
    if (item.stores & info.rgbitStores[i])
      last = i;
  for (i = 0; i < info.cStores; i++)
    if (item.stores & info.rgbitStores[i])
      fprintf(out, "%s%s", QUOTE(info.rgszStoreNames[i]), i < last ? ", " : " ");
  fprintf(out, "}");
  fprintf(out, "\n      }");
}
Defines BIT, dumphsr (links are to index).

<functions>+= (<-U) [<-D]
static char *infodoc[] = { 0 };
static char *recorddoc[] = { 0 };
static char *trailers[] = {
  0
};


struct PDBConv handyShopper = {
  "HandyShop shopping list",
  "Data", "iShp",
  { infodoc, dumphsi, pack },
  { 0, generic_unpackInfo, generic_packInfo },
  { recorddoc, dumphsr, packr }, 
  trailers
};
Defines handyShopper, infodoc, recorddoc, trailers (links are to index).