#include <sys/types.h>
#include <unistd.h>
#include "strerr.h"
#include "uint32.h"
#include "buffer.h"
#include "fmt.h"

#define USAGE ""
#define FATAL "cdbdump: fatal: "

#define BUFSIZE 1024

const char *progname;
char buf[BUFSIZE];

void usage() { strerr_die4x(100, "usage: ", progname, USAGE, "\n"); }

void getb(char *b, unsigned int len) {
  unsigned int l;

  while (len) {
    switch ((l =buffer_get(buffer_0, b, len))) {
    case 0: strerr_die2x(111, FATAL, "unable to read input: truncated file");
    case -1: strerr_die2sys(111, FATAL, "unable to read input: ");
    }
    len -=l; b +=l;
  }
}
void put1(char *s, unsigned int l) {
  if (buffer_put(buffer_1, s, l) == -1)
    strerr_die2sys(111, FATAL, "unable to write output: ");
}

void copyb(uint32 len) {
  uint32 l;

  while (len) {
    l =len < BUFSIZE ? len : BUFSIZE;
    getb(buf, l);
    put1(buf, l);
    len -=l;
  }
}

int main(int argc, char **argv) {
  uint32 pos;
  uint32 eodata;
  uint32 keylen;
  uint32 datalen;
  char num[FMT_ULONG];

  progname =*argv++;
  if (*argv) usage();
  pos =0;

  /* read data size */
  getb(buf, 4);
  pos +=4;
  uint32_unpack(buf, &eodata);
  /* data starts after 2048 bytes */
  while (pos < 2048) {
    getb(buf, (2048 -pos) < BUFSIZE ? (2048 -pos) : BUFSIZE);
    pos +=(2048 -pos) < BUFSIZE ? (2048 -pos) : BUFSIZE;
  }

  /* read keylen, datalen, key, data up to data size */
  while (pos < eodata) {
    /* keylen */
    getb(buf, 4);
    pos +=4;
    uint32_unpack(buf, &keylen);
    /* datalen */
    getb(buf, 4);
    pos +=4;
    uint32_unpack(buf, &datalen);
    /* format output keylen, datalen */
    put1("+", 1);
    put1(num, fmt_ulong(num, keylen));
    put1(",", 1);
    put1(num, fmt_ulong(num, datalen));
    put1(":", 1);
    /* format output key and data */
    copyb(keylen);
    pos +=keylen;
    put1("->", 2);
    copyb(datalen);
    pos +=datalen;
    put1("\n", 1);
    /* next entry */
  }

  /* final empty line */
  put1("\n", 1);
  if (buffer_flush(buffer_1) == -1)
    strerr_die2sys(111, FATAL, "unable to flush output: ");
  _exit(0);
}
