/* dumpftree.c   by Michael Thorpe   2002-08-16 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ftree.h"

typedef int ftreevalue;
typedef unsigned int idx;

struct head {
   idx freetail,free,root;
   unsigned char magic[3];
};

struct node {
   idx child,next;
   ftreevalue value;
   char s[1];
};

struct hole {
   idx prev,next;
   unsigned char size;
};

#define MAX_LEN 15
struct stats {
   unsigned int numnodes,numholes,numerrs;
   unsigned int nodesize[MAX_LEN+1];
   unsigned int holesize[MAX_LEN+1];
   unsigned int slen[MAX_LEN+1];
};

int dumper(const char *s,int i) {
   printf("%d\t%s\n",i,s);
   return(0);
}

int main(int argc,char **argv) {
   int fd,i,instr,size,skip,showhead=0,showstats=0,slen=-42,verbose=0;
   idx l=0,lasthole,lastholenext,freetail;
   unsigned char buf[16];
   struct head *head;
   struct node *node;
   struct hole *hole;
   struct stats stats;
   ftree *tree;

   for(i=1;argc>i && argv[i][0]=='-';i++) {
      for(fd=1;argv[i][fd];fd++)
         switch(argv[i][fd]) {
         case 'h': showhead=1; break;
         case 's': showstats=1; break;
         case 'v': verbose=showhead=1; break;
         default: goto badarg;
         }
   }
badarg:
   if(argc != i+1) {
      fprintf(stderr,"usage: %s [-hsv] <ftreefile>\n",argv[0]);
      return(1);
   }
   if(showhead || showstats || verbose) {
      memset(&stats,0,sizeof(stats));
      fd=open(argv[i],O_RDONLY);
      if(fd<0) {
         perror("open");
         return(-1);
      }
      head=(struct head *)buf;
      node=(struct node *)buf;
      hole=(struct hole *)buf;
      if(16 != read(fd,buf,16)) {
         perror("read");
         return(-1);
      }
      if(showhead) {
         printf("head\tt=%-3u\tf=%-3u\tr=%-3u\tmagic=%c%c(",head->freetail,head->free,head->root,head->magic[0],head->magic[1]);
         instr=head->magic[2];
         for(skip=128;skip;skip>>=1)
            if(instr&skip)
               putchar('1');
            else
               putchar('0');
         putchar(')');
         putchar('\n');
      }
      if(!showstats && !verbose)
         return(close(fd));
      size=buf[15];
      lasthole=0;
      lastholenext=head->free;
      freetail=head->freetail;
      for(instr=skip=0;16==read(fd,buf,16);size=buf[15]) {
         l++;
         if(skip) {
            skip--;
            if(instr>1) {
               if(verbose)
                  i=printf("%.16s",buf);
               else
                  for(i=0;i<16 && buf[i];i++)
                     ;
               slen+=i;
               if(16 != i)
                  instr=1;
            }
            continue;
         }
         if(instr) {
            if(verbose)
               printf("\"\n");
            if(slen>MAX_LEN)
               stats.slen[MAX_LEN]++;
            else
               stats.slen[slen]++;
            instr=0;
         }
         if(size) {
            stats.numnodes++;
            if(size>MAX_LEN)
               stats.nodesize[MAX_LEN]++;
            else
               stats.nodesize[size]++;
            if(verbose)
               printf("node%-3u\tc=%u\tn=%-3u\tv=%-3d\ts=\"%.4s",l,node->child,node->next,node->value,node->s);
            skip=size-1;
            instr=1;
            for(slen=0;slen<4 && node->s[slen];slen++)
               ;
            if(slen==4)
               instr=2;
         } else {
            stats.numholes++;
            if(hole->size+1>MAX_LEN)
               stats.holesize[MAX_LEN]++;
            else
               stats.holesize[hole->size+1]++;
            if(verbose) {
               printf("hole%-3u\tp=%-3u\tn=%-3u\ts=%-3u",l,hole->prev,hole->next,hole->size);
               if(lasthole != hole->prev)
                  printf("\t***%u ISN'T PREV***",lasthole);
               if(lastholenext != l)
                  printf("\t***%u WAS NEXT***",lastholenext);
               putchar('\n');
            } else {
               if(lasthole != hole->prev)
                  stats.numerrs++;
               if(lastholenext != l)
                  stats.numerrs++;
            }
            skip=hole->size;
            lasthole=l;
            lastholenext=hole->next;
         }
      }
      if(lasthole != freetail || lastholenext != 0) {
         if(lasthole != freetail)
            if(verbose)
               printf("\t***%u(LAST) ISN'T %u(TAIL)***",lasthole,freetail);
            else
               stats.numerrs++;
         if(lastholenext != 0)
            if(verbose)
               printf("\t***%u->next ISN'T 0***",lastholenext);
            else
               stats.numerrs++;
         printf("\n");
      }
      if(showstats) {
         printf("numnodes=%d\tnumholes=%d\n",stats.numnodes,stats.numholes);
         for(i=0;i<MAX_LEN;i++)
            printf("%3d: %7d %7d %7d\n",i,stats.nodesize[i],stats.holesize[i],stats.slen[i]);
         printf("%2d+: %7d %7d %7d\n",i,stats.nodesize[i],stats.holesize[i],stats.slen[i]);
      }
      if(stats.numerrs)
         printf("*** DETECTED %d ERRORS! ***\n",stats.numerrs);
      return(close(fd));
   } else {
      if(!(tree=ftree_open(argv[i],0)) || ftree_readlock(tree)) {
         fprintf(stderr,"unable to open and lock %s\n",argv[i]);
         return(1);
      }
      if(ftree_dumpfull(tree,dumper)) {
         fprintf(stderr,"unable to dump tree\n");
         return(1);
      }
      if(ftree_readunlock(tree) || ftree_close(tree)) {
         fprintf(stderr,"unable to unlock and close %s\n",argv[i]);
         return(1);
      }
   }
   return(0);
}
