/*
 * the windows 95 registry file format
 *
 *  by W.J.Hengeveld apr 1998
 *     itsme@xs4all.nl
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>
#include <share.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>

#ifndef TRUE
#define TRUE (1==1)
#define FALSE (!TRUE)
#endif

#ifndef bool
typedef int bool;
#endif

#ifndef uint8
typedef unsigned char  uint8;
typedef unsigned short uint16;
typedef unsigned long  uint32;
#endif
#ifndef sint8
typedef signed char    sint8;
typedef signed short   sint16;
typedef signed long    sint32;
#endif

#define ERR_EOF   -1
#define ERR_SIG   -2
#define ERR_UNK   -3

// conventions:
//     - put global variables in gl struct.
//     - variable signifying # bytes end in _b
//     - variable signifying absolute offset end in _o
//     - variable signifying relative offset end in _ro
//     - variable signifying a count of things start with n_


struct {
   struct {
      bool verbose;
      bool debug;
      bool printspare;
      bool printvalue;
   } flag;
   int n_files;
   char **fn;
   struct {
      uint32 total_spare_b;
      uint32 total_defaults;
      uint32 total_values;
      uint32 total_keys;
      uint32 rgkn_entries;
   } stats;
   struct key_list {
      uint32 id;       // from rgdb
      uint32 ofs;
      uint16 len;
   } *list;
   int kIdx;
   int listMax;
} gl;

enum {
  NEED_ARG_NONE,
  NEED_ARG_VALUE,
  NEED_ARG_STRING
};

struct need_arg {
   int atype;
   union {
      uint32 *value;
      char **string;
   } ptr;
};

#define SIG_CREG  0x47455243L
#define SIG_RGKN  0x4e4b4752L
#define SIG_RGDB  0x42444752L

struct registry_creg {
   uint32 signature;
   uint16 unknown1;
   uint16 unknown2;
   uint32 first_rgdb_o;
   uint32 unknown3;
   uint16 n_rgdb;
   uint8  unknown4[14];
};

struct registry_rgkn {
   uint32 signature;
   uint32 alloced_b;
   uint32 first_ro;
   uint32 used_b;
   uint32 unknown5[7];
};

//
struct rgkn_entry {
   uint32 parent_ro;
   uint32 first_child_ro;
   uint32 next_sibling_ro;
   uint32 rgdb_id;
// flag is aligned on page boundaries!!
   uint32 flag;            // 0x80000000 for last entry
   uint32 unknown12[2];    // unknown12[1] = always 0xffffffff
};

struct registry_rgdb {
   uint32 signature;
   uint32 alloced_b;
   uint32 free_b;
   uint16 unknown6;
   uint16 rgdb_nr;
   uint32 used_b;
   uint16 max_id;
   uint16 n_ids;
   uint8 unknown11[8];
};

struct registry_rgdbend {
   uint32  rgdbend_b;
   sint32  term1;
   sint32  term2;
};

// the RGDB part is organized as follows:
//    struct registry_key key;
//    char   key_name[key.name_len];
//    struct data {
//       struct registry_value value;
//       char value_name[value.name_len];
//       uint8 value_name[value.val_len];
//    } data[key.num_values];
//    uint8  spare[spare_len];
//
//  such that   &spare - &key = key.used_b
//  and         spare_len = key.alloced_b - key.used_b
//
//  and  ofs(key) < creg.first_rgdb_o+rgdb.used_b

struct registry_key {
   uint32 alloced_b;
   uint32 rgdb_id;             // some sort of id
   uint32 used_b;
   uint16 name_len;
   uint16 num_values;
   uint32 unknown8;             // always 0!
};

#define REGTYPE_0        0
#define REGTYPE_STRING   1
#define REGTYPE_HEX      3
#define REGTYPE_VALUE    4
#define REGTYPE_7        7

struct registry_value {
   uint16 vtype;              // REGTYPE_
   uint8 unknown9[6];         // always 0
   uint16 name_len;
   uint16 val_len;
};

/*****************************************************************************
 *
 *
 */
void dump_hex(int fh, uint8 *p, uint32 start_o, uint32 len)
{
   uint8 buf[64];
   int   cur_buf_len=0;
   uint32 cur_bufend_o;
   int n;
   int   cur_ro=0;

   uint8 c;

   cur_bufend_o=start_o;

   while (len--)
   {
      if (fh>=0)
      {
         if (cur_ro>=cur_buf_len)
         {
            lseek(fh, cur_bufend_o, SEEK_SET);
            n=read(fh, buf, min(sizeof(buf), len));
            if (n<0) return;
            cur_buf_len=n;
            cur_bufend_o+=cur_buf_len;
            cur_ro=0;
         }
         c=buf[cur_ro++];
      }
      else if (p!=NULL)
         c=p[cur_bufend_o++];
      else
         return;
      printf(" %02x", c);
   }
}

void dump_ascii(int fh, uint8 *p, uint32 start_o, uint32 len)
{
   uint8 buf[64];
   int   cur_buf_len=0;
   uint32 cur_bufend_o;
   int n;
   int   cur_ro=0;

   uint8 c;

   cur_bufend_o=start_o;

   while (len--)
   {
      if (fh>=0)
      {
         if (cur_ro>=cur_buf_len)
         {
            lseek(fh, cur_bufend_o, SEEK_SET);
            n=read(fh, buf, min(sizeof(buf), len));
            if (n<0) return;
            cur_buf_len=n;
            cur_bufend_o+=cur_buf_len;
            cur_ro=0;
         }
         c=buf[cur_ro++];
      }
      else if (p!=NULL)
         c=p[cur_bufend_o++];
      else
         return;
      putchar(isprint(c)?c:'.');
   }
}

void dump_mixed(int fh, uint8 *p, uint32 start_o, uint32 len)
{
   bool in_quotes=FALSE;
   uint8 buf[64];
   int   cur_buf_len=0;
   uint32 cur_bufend_o;
   int n;
   int   cur_ro=0;

   uint8 c;

   cur_bufend_o=start_o;

   while (len--)
   {
      if (fh>=0)
      {
         if (cur_ro>=cur_buf_len)
         {
            lseek(fh, cur_bufend_o, SEEK_SET);
            n=read(fh, buf, min(sizeof(buf), len));
            if (n<0) return;
            cur_buf_len=n;
            cur_bufend_o+=cur_buf_len;
            cur_ro=0;
         }
         c=buf[cur_ro++];
      }
      else if (p!=NULL)
         c=p[cur_bufend_o++];
      else
         return;

      if (isprint(c))
      {
         if (!in_quotes)
         {
            printf(" \"");
            in_quotes=TRUE;
         }
         putchar(c);
      }
      else
      {
         if (in_quotes)
         {
            printf("\"");
            in_quotes=FALSE;
         }
         printf(" %02x", c);
      }
   }
   if (in_quotes)
   {
      printf("\"");
      in_quotes=FALSE;
   }
}

void printspaces(int n)
{
   while (n--) putchar(' ');
}

// format string:
//    %No    - offset N digits
//    %N.    - set 'N' for N bytes per dump line
//    %h     - hexdump N bytes
//    %a     - ascii N characters
//    %m     - mixed N characters
//    %-Ns   - insert string from parameter list, left adjusted in N chars
//    %+Ns   - insert string from parameter list, right adjusted in N chars
//    %s     - insert string from parameter list
//
//   when no N is specified for 'h', 'a' or 'm'  then everything will be printed
//   on one line

void vxdumpf(int fh, uint8 *buf, uint32 start_o, uint32 len, char *fmt, va_list ap)
{
   struct dump_flags {
      uint32  fixed;
      uint32  offset;
      int     offset_width;

      struct string_list {
         char *p;
         char str[16];
         int  width;      // <0 : leftadj, ==0: no field, >0: rightadj
      } str[8];
      int   n_str;

      char format[32];
      int  n_fmt;
   } flag;
   char *p, *q;
   int number1;
//   int number2;
   uint32 cur_ro;
   int i;
   int stridx;
   uint32 prtlen;
   memset(&flag, 0, sizeof(flag));

   p=fmt;
   while (*p)
   {
      if (*p=='%')
      {
         p++;
         number1=0;
//         number2=0;
         if (isdigit(*p) || *p=='-')
         {
            number1=(int)strtol(p, &q, 10);
            p=q;
//            if (*p==';')
//            {
//               number2=strtol(p+1, &q, 10);
//               p=q;
//            }
         }

         switch(*p)
         {
            case 'o':   // offset
               flag.offset=va_arg(ap, uint32);
               flag.offset_width=number1;
               flag.format[flag.n_fmt++]=*p;
               break;
            case 's':   // string
               flag.str[flag.n_str].p=va_arg(ap, char*);
               flag.str[flag.n_str].width=number1;
               flag.n_str++;
               flag.format[flag.n_fmt++]=*p;
               break;
            case 'h':   // hex
               flag.format[flag.n_fmt++]=*p;
               break;
            case 'a':   // ascii
               flag.format[flag.n_fmt++]=*p;
               break;
            case 'm':   // mixed
               flag.format[flag.n_fmt++]=*p;
               break;
            case '.':   // set fixed nr
               flag.fixed=number1;
               break;
            default:    //
               fprintf(stderr, "Error in format string \"%s\"\n", fmt);
               exit(1);
         }
         p++;
      }
      else
      {
         q=strchr(p, '%');
         if (q==NULL)
            q=strchr(p, 0);
         if (q-p>sizeof(flag.str[flag.n_str].str))
         {
            fprintf(stderr, "overflow Error in format string \"%s\"\n", fmt);
            exit(1);
         }
         memcpy(flag.str[flag.n_str].str, p, q-p);
         flag.str[flag.n_str].str[q-p]=0;
         flag.str[flag.n_str].width=0;
         flag.str[flag.n_str].p=flag.str[flag.n_str].str;
         flag.n_str++;
         flag.format[flag.n_fmt++]='s';

         p+=q-p;
      }
   }

   if (gl.flag.debug)
   {
      printf("fix=%ld off=%08lx ofw=%d nstr=%d nfmt=%d fmt=\"%s\"\n",
         flag.fixed, flag.offset, flag.offset_width, flag.n_str, flag.n_fmt, flag.format);
      for (i=0 ; i<flag.n_str ; i++)
      {
         printf("  %d (w=%d)\"%s\"\n", i, flag.str[i].width, flag.str[i].p);
      }
   }

   cur_ro=0;
   while (cur_ro<len)
   {
      stridx=0;
      if (flag.fixed)
         prtlen=min(flag.fixed, len-cur_ro);
      else
         prtlen=len-cur_ro;
      for (p=flag.format ; *p ; p++)
      {
         switch(*p)
         {
            case 'o':
               printf("%0*lx", flag.offset_width, flag.offset+cur_ro);
               break;
            case 's':
               if (flag.str[stridx].width<0)
                  printf("%-*.*s", flag.str[stridx].width, flag.str[stridx].width, flag.str[stridx].p);
               else if (flag.str[stridx].width>0)
                  printf("%*.*s", flag.str[stridx].width, flag.str[stridx].width, flag.str[stridx].p);
               else
                  printf("%s", flag.str[stridx].p);
               stridx++;
               break;
            case 'h':
               dump_hex(fh, buf, start_o+cur_ro, prtlen);
               if (flag.fixed)
                  printspaces(3*(flag.fixed-prtlen));
               break;
            case 'a':
               dump_ascii(fh, buf, start_o+cur_ro, prtlen);
               if (flag.fixed)
                  printspaces(1*(flag.fixed-prtlen));
               break;
            case 'm':
               dump_mixed(fh, buf, start_o+cur_ro, prtlen);
               break;
         }
      }
      cur_ro+=prtlen;
   }
}

void vdumpf(uint8 *buf, uint32 len, char *fmt, va_list ap)
{
   vxdumpf(-1, buf, 0, len, fmt, ap);
}

void dumpf(uint8 *buf, uint32 len, char *fmt, ...)
{
   va_list ap;
   va_start(ap, fmt);
   vdumpf(buf, len, fmt, ap);
   va_end(ap);
}

// !!!!! do something to correct double quotes
int fdumpf(int fh, uint32 ofs, uint32 len, char *fmt, ...)
{
   va_list ap;

   va_start(ap, fmt);
   vxdumpf(fh, NULL, ofs, len, fmt, ap);
   va_end(ap);
   return 0;
#if 0
   uint8 buf[64];
   int  n=0;
   while (len)
   {
      lseek(fh, ofs, SEEK_SET);
      n = read(fh, buf, (int)min(sizeof(buf),len));
      if (n<0) goto eof_exit;
      va_start(ap, fmt);
      vdumpf(buf, n, fmt, ap);
      va_end(ap);
      len-=n;
      ofs+=n;
   }
   return 0;

eof_exit:
   return ERR_EOF;
#endif
}

/*****************************************************************************
 *
 *
 */
void print_rgkn_st(struct registry_rgkn *rgkn)
{
   int i;
   printf("RGKN: signature=%08lx size=%08lx used=%08lx first=%08lx\n",
         rgkn->signature, rgkn->alloced_b, rgkn->used_b, rgkn->first_ro);
   printf("      ?5=");
   for (i=0 ; i<7 ; i++)
      printf(" %08lx", rgkn->unknown5[i]);
   printf("\n");
}

void print_rgkn_ent(struct rgkn_entry *rgknent)
{
   printf("id=%08lx ",  rgknent->rgdb_id);
   if (rgknent->parent_ro==0xffffffffL)
      printf("pnt=______ ");
   else
      printf("pnt=%06lx ", rgknent->parent_ro);
   if (rgknent->next_sibling_ro==0xffffffffL)
      printf("nxt=______ ");
   else
      printf("nxt=%06lx ",rgknent->next_sibling_ro);
   if (rgknent->first_child_ro==0xffffffffL)
      printf("chl=______ ");
   else
      printf("chl=%06lx ", rgknent->first_child_ro);

   printf("?12=%08lx", rgknent->unknown12[0]);

   if (rgknent->unknown12[1]!=0xffffffffL)
      printf(" !12:1=%08lx", rgknent->unknown12[1]);

   if (rgknent->flag!=0 && rgknent->flag!=0x80000000L)
      printf(" !flag=%08lx", rgknent->flag);
   printf("\n");

   if (rgknent->flag==0x80000000L)
      printf("<EOF>\n");
}

int process_rgkn(int fh, uint32 rgkn_o, struct registry_creg *creg)
{
   struct registry_rgkn rgkn;
   int n;
   struct rgkn_entry rgknent;
   uint32 ofs;
   int rc=0;
   uint32 max_o;
   uint32 first_ent_o;
   int fl_align;

   lseek(fh, rgkn_o, SEEK_SET);
   n = read(fh, &rgkn, sizeof(rgkn));
   if (n<0) goto eof_exit;
   printf("%08lx: ", rgkn_o);
   print_rgkn_st(&rgkn);
   if (rgkn.signature!=SIG_RGKN)
      goto err_invsig;

   if (rgkn_o+ rgkn.alloced_b != creg->first_rgdb_o)
      printf("     !!!!! rgkn_o+alloc != rgdb start\n");

   ofs=rgkn_o+n;
   first_ent_o=ofs;

   max_o=rgkn.used_b+first_ent_o-rgkn.first_ro;
   while (!eof(fh) && ofs<max_o)
   {
      lseek(fh, ofs, SEEK_SET);
      n = read(fh, &rgknent, sizeof(rgknent));
      if (n<0) goto eof_exit;

// rgknent.flag has to be page boundary aligned
      fl_align= (ofs-rgkn_o+sizeof(rgknent))%0x1000;
      if (fl_align <= 4)
      {
         fdumpf(fh, ofs+0x10, 3*4-fl_align, "%8o !! %h - %a\n", ofs+0x10);
         n-=fl_align;
         lseek(fh, ofs+n, SEEK_SET);
         n+=read(fh, &rgknent.flag, 3*4);
      }
      printf("%08lx K%06lx:  ", ofs, ofs-first_ent_o+rgkn.first_ro);
      print_rgkn_ent(&rgknent);
      ofs+=n;
      gl.stats.rgkn_entries++;
   }

   printf("%08lx   %ld keys in rgkn,  spare in rgkn: %8ld\n",
         ofs, gl.stats.rgkn_entries, creg->first_rgdb_o - ofs);
   gl.stats.total_spare_b += creg->first_rgdb_o  - ofs;
   if (gl.flag.printspare)
   {
      printf("%08lx: SPARE\n", ofs);
      fdumpf(fh, ofs, creg->first_rgdb_o - ofs, " S  %6o %16.%h - %a\n", ofs);
      if (rc) goto eof_exit;
      else if (rc) goto err_unknown;
   }
   return 0;

err_invsig:
   printf("Invalid signature\n");
   return ERR_SIG;

err_unknown:
   printf("Unknown error: %d\n", rc);
   return ERR_UNK;

eof_exit:
   return ERR_EOF;
}

/*****************************************************************************
 *
 *
 */
void print_rgdb_st(struct registry_rgdb *rgdb)
{
   int i;
   printf("RGDB: signature=%08lx  used=%08lx free=%08lx alloc=%08lx\n",
         rgdb->signature,
         rgdb->used_b, rgdb->free_b, rgdb->alloced_b);
   printf("      n_ids=%d rgdb_nr=%04x max_id=%04x\n",
         rgdb->n_ids, rgdb->rgdb_nr, rgdb->max_id);
   if (rgdb->used_b + rgdb->free_b != rgdb->alloced_b)
      printf("      !!!!!used + free != alloced\n");
   printf("      ?6=%04x  ?11=", rgdb->unknown6);
   for (i=0 ; i<8 ; i++)
      printf(" %02x", rgdb->unknown11[i]);
   printf("\n");
}

void print_key_st(struct registry_key *key)
{
   if (gl.flag.verbose)
   {
      printf("next=%08lx spare=%08lx nlen=%2d nval=%2d  id=%08lx",
         key->alloced_b,
         key->used_b,
         key->name_len,
         key->num_values,
         key->rgdb_id);
      if (key->unknown8!=0)
         printf(" !!?8=%08lx", key->unknown8);
   }
   else
   {
      printf("#=%3d %08lx ",
         key->num_values, key->rgdb_id);
   }
}

void print_value_st(struct registry_value *value)
{
   int i;

   printf("  VALST: type=%d nlen=%2d vlen=%3d",
         value->vtype, value->name_len, value->val_len);

   for (i=0 ; i<6 ; i++)
      if (value->unknown9[i])
         break;
   if (i<6)
      dumpf(value->unknown9, 6, "          ?9=%h");

   printf("\n");
}

int process_rgdb(int fh, uint32 rgdb_o, uint32 *pLen)
{
   struct registry_rgdb rgdb;
   struct registry_key key;
   struct registry_value value;
   struct registry_rgdbend rgdbend;
   uint32 ofs;
   uint32 max_o;
   int i;
   int n;
   int rc=0;
   uint32 spare_bytes=0;

   printf("%08lx: ", rgdb_o);
   ofs=rgdb_o;

   lseek(fh, rgdb_o, SEEK_SET);
   n=read(fh, &rgdb, sizeof(rgdb));
   if (n<=0) goto eof_exit;
   if (n!=sizeof(rgdb)) goto err_unknown;

   if (rgdb.signature!=SIG_RGDB)
      goto err_invsig;

   print_rgdb_st(&rgdb);

   *pLen=rgdb.alloced_b;

   ofs+=n;
   if (rgdb.used_b==0xffffffffL)
      max_o=rgdb_o+rgdb.alloced_b;
   else
      max_o=rgdb_o+rgdb.used_b;

   while (!eof(fh) && ofs<max_o)
   {
      printf("%08lx: ", ofs);
      lseek(fh, ofs, SEEK_SET);
      n=read(fh, &key, sizeof(key));
      if (n<0) goto eof_exit;
      ofs+=n;

      print_key_st(&key);
      gl.stats.total_keys++;

      rc = fdumpf(fh, ofs, key.name_len, "%m\n");
      if (rc==ERR_EOF) goto eof_exit;
      else if (rc) goto err_unknown;

      if (gl.kIdx<gl.listMax)
      {
         gl.list[gl.kIdx].id=key.rgdb_id;
         gl.list[gl.kIdx].ofs=ofs;
         gl.list[gl.kIdx].len=key.name_len;
         gl.kIdx++;
      }
      else
         printf("!!!not enough space in keylist\n");
      ofs+=key.name_len;

      gl.stats.total_values+=key.num_values;
      for (i=0 ; i<key.num_values ; i++)
      {
         lseek(fh, ofs, SEEK_SET);
         n=read(fh, &value, sizeof(value));
         if (n<0) goto eof_exit;
         ofs+=n;

         if (gl.flag.verbose)
            print_value_st(&value);

         if (value.name_len==0)
            gl.stats.total_defaults++;

         rc = fdumpf(fh, ofs, value.name_len, "    NAME=%m\n");
         if (rc) goto eof_exit;
         else if (rc) goto err_unknown;

         ofs+=value.name_len;

         if (gl.flag.printvalue)
         {
            if (value.vtype==REGTYPE_STRING)
            {
               rc = fdumpf(fh, ofs, value.val_len, "    VALUESTR=%m\n");
               if (rc) goto eof_exit;
               else if (rc) goto err_unknown;
            }
            else if (value.vtype==REGTYPE_HEX)
            {
               printf("    VALUE=\n");
               rc = fdumpf(fh, ofs, value.val_len, "    V  %6o %16.%h - %a\n", ofs);
               if (rc) goto eof_exit;
               else if (rc) goto err_unknown;
            }
            else if (value.vtype==REGTYPE_VALUE)
            {
               rc = fdumpf(fh, ofs, value.val_len, "    VALUEHEX=%6o %16.%h - %a\n", ofs);
               if (rc) goto eof_exit;
               else if (rc) goto err_unknown;
            }
            else
            {
               printf("    VALUEUNK%d=", value.vtype);
               rc = fdumpf(fh, ofs, value.val_len, "    U  %6o %16.%h - %a\n", ofs);
               if (rc) goto eof_exit;
               else if (rc) goto err_unknown;
            }
         }
         ofs+=value.val_len;
      }
      spare_bytes+=key.alloced_b - key.used_b;
      if (gl.flag.printspare)
      {
         printf("%08lx: SPARE\n", ofs);
         rc = fdumpf(fh, ofs, key.alloced_b - key.used_b, " S  %6o %16.%h - %a\n", ofs);
         if (rc) goto eof_exit;
         else if (rc) goto err_unknown;
      }
      else if (gl.flag.verbose)
      {
         printf("%08lx: SPARE %ld bytes\n", ofs, key.alloced_b - key.used_b);
      }
      ofs+=key.alloced_b - key.used_b;
   }
   printf("       total spare in data area: %ld bytes\n", spare_bytes);

   gl.stats.total_spare_b += spare_bytes;

   printf("%08lx  -----end of rgdb", ofs);
   if (ofs<rgdb_o+rgdb.alloced_b)
   {
      lseek(fh, ofs, SEEK_SET);
      n=read(fh, &rgdbend, sizeof(rgdbend));
      if (n<0) goto eof_exit;
      ofs+=n;
      printf("  %d bytes left ", rgdbend.rgdbend_b);

      if (rgdbend.rgdbend_b!=rgdb.free_b) printf(" !!!endsize!=freesize");
      if (rgdbend.term1!=-1) printf(" term1=%08lx", rgdbend.term1);
      if (rgdbend.term2!=-1) printf(" term2=%08lx", rgdbend.term2);

      gl.stats.total_spare_b += rgdbend.rgdbend_b;
   }
   printf("\n");
   if (gl.flag.verbose)
   {
      if (gl.flag.debug) printf("tell=%08lx\n", tell(fh));
      printf("%08lx: ENDSPARE %ld bytes\n", ofs, rgdbend.rgdbend_b-sizeof(rgdbend));
      rc = fdumpf(fh, ofs, rgdbend.rgdbend_b-sizeof(rgdbend), " E  %6o %16.%h - %a\n", ofs);
      if (rc) goto eof_exit;
      else if (rc) goto err_unknown;

   }
   ofs+=rgdbend.rgdbend_b-sizeof(rgdbend);
   return 0;

err_invsig:
   printf("Invalid signature\n");
   return ERR_SIG;

err_unknown:
   printf("Unknown error: %d\n", rc);
   return ERR_UNK;

eof_exit:
   return ERR_EOF;
}

/*****************************************************************************
 *
 *
 */
void print_creg(struct registry_creg *creg)
{
   printf("CREG: sig=%08lx ?1=%04x ?2=%04x ?3=%08lx  rgdb=%08lx, nrgdb=%d\n",
      creg->signature, creg->unknown1, creg->unknown2, creg->unknown3,
      creg->first_rgdb_o, creg->n_rgdb);
   dumpf(creg->unknown4, 14, "      ?4=%h\n");
}


//  0x0000           = struct registry_creg creg
//       len = sizeof(struct registry_creg)
//  0x0020           = struct registry_rgkn rgkn
//       len = rgkn.alloced_b
//  creg.first_rgdb_o = struct registry_rgdb rgdb[creg.n_rgdb];
//       len = rgdb.alloced_b

int process_registry(int fh, uint32 creg_o)
{
   struct registry_creg creg;
   int n;
   int rc;
   int rgdbcount=0;
   uint32 ofs;
   uint32 len;
   int  i;

   gl.stats.total_spare_b=0;
   ofs=creg_o;

   lseek(fh, creg_o, SEEK_SET);
   n=read(fh, &creg, sizeof(creg));
   if (n<0) goto eof_exit;
   if (creg.signature!=SIG_CREG)
      goto err_invsig;

   print_creg(&creg);

   gl.listMax=255*creg.n_rgdb;
   gl.list=malloc(sizeof(*gl.list)*gl.listMax);
   if (gl.list==NULL)
   {
      printf("Out of memory\n");
      goto err_unknown;
   }
   gl.kIdx=0;

   rc = process_rgkn(fh, 0x20, &creg);
   if (rc==ERR_EOF) goto eof_exit;
   else if (rc) goto err_unknown;

   gl.stats.total_defaults=0;
   gl.stats.total_values=0;
   gl.stats.total_keys=0;

   ofs=creg.first_rgdb_o;
   for (i=0 ; i<creg.n_rgdb ; i++)
   {
      printf("------------RGDB#%d--------------\n", rgdbcount++);
      rc = process_rgdb(fh, ofs, &len);
      if (rc) goto eof_exit;

      ofs+=len;       // !!!! cheating here
   }

   free(gl.list);
   gl.list=NULL;
   gl.kIdx=0;

   printf("total unused space in registry: %ld bytes\n", gl.stats.total_spare_b);
   printf("  total defaults: %ld\n", gl.stats.total_defaults);
   printf("  total values: %ld\n", gl.stats.total_values);
   printf("  total keys: %ld\n", gl.stats.total_keys);
   printf("  total rgknents %ld\n", gl.stats.rgkn_entries);
   return 0;

err_invsig:
   printf("Invalid signature\n");
   return ERR_SIG;

err_unknown:
   printf("Unknown error: %d\n", rc);
   return ERR_UNK;

eof_exit:
   return ERR_EOF;
}

void usage(void)
{
   printf("Usage: reg [options] file\n");
   printf("   -v:  verbose\n");
   printf("   -d:  debug\n");
   printf("   -p:  printvalue\n");
   printf("   -s:  printspare\n");
}

/*****************************************************************************
 *
 *
 */
int main(int argc, char **argv)
{
   int i;
   char *argp;
   bool end_options=FALSE;
   struct need_arg  arg={NEED_ARG_NONE, NULL};
   int fh=-1;
   int rc=0;

   for (i=1 ; i<argc ; i++)
   {
      argp=argv[i];
      if (*argp=='-' && !end_options)
      {
         argp++;
         if (*argp==0)
         {
            end_options++;
            continue;
         }
         while (*argp)
         {
            switch(*argp)
            {
               case 'v': gl.flag.verbose=TRUE;  break;
               case 'd': gl.flag.debug=TRUE;  break;
               case 'p': gl.flag.printvalue=TRUE; break;
               case 's': gl.flag.printspare=TRUE; break;
               default:
                  printf("Unknown switch: (%c) in %s\n", *argp, argv[i]);
                  usage();
                  rc=1; goto error_exit;
            }
            argp++;
         }
      }
      else if (arg.atype==NEED_ARG_STRING)
      {
         *arg.ptr.string=argp;
         arg.atype=NEED_ARG_NONE;
      }
      else if (arg.atype==NEED_ARG_VALUE)
      {
         *arg.ptr.value=strtol(argp, 0, 0);
         arg.atype=NEED_ARG_NONE;
      }
      else
      {
         void *newptr;
         gl.n_files++;
         if (gl.fn==NULL)
            newptr=malloc(gl.n_files*sizeof(char*));
         else
            newptr=realloc(gl.fn, gl.n_files*sizeof(char*));
         if (newptr==NULL)
         {
            printf("Out of memory allocating memory for commandline\n");
            rc=1; goto error_exit;
         }
         gl.fn=(char **)newptr;
         gl.fn[gl.n_files-1]=argp;
      }
   }

   for (i=0 ; i<gl.n_files ; i++)
   {
      fh=open(gl.fn[i],O_RDONLY|O_BINARY|SH_DENYWR);
      if (fh<0)
      {
         perror(gl.fn[i]);
         rc=1;
         goto error_exit;
      }
      rc = process_registry(fh, 0L);
      if (rc)
      {
         printf("Error %d processing registry file %s\n", rc, gl.fn[i]);
      }
      close(fh);
      fh=-1;
   }
   rc=0;

error_exit:
   if (fh>=0)  { close(fh); fh=-1; }
   if (gl.fn!=NULL) { free(gl.fn); gl.fn=NULL; }
   if (gl.list!=NULL) { free(gl.list); gl.list=NULL; }
   return rc;
}
