/***************************************************************************
 *                                                                         *
 *   WGSRIDX.C                                                             *
 *                                                                         *
 *   Copyright (C) 1988-1997 Galacticomm, Inc.     All Rights Reserved.    *
 *                                                                         *
 *   This is a Btrieve file fixer-upper utility.                           *
 *                                                                         *
 *                                            - T. Stryker 9/14/88         *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "majorbbs.h"
#include "excphand.h"

#define FILREV "$Revision: 23 $"

static INT scnup=0;                /* is the WGSRIDX screen up?            */
static CHAR dosscn[GVIDSCNSIZ];    /* saved DOS screen image               */
extern CHAR scntbl[][GVIDSCNSIZ];/* array of screens (c/o MAKESCNS)*/

DFAFILE *svbb,                /* Btrieve file block ptr for sys variables  */
        *adbb=NULL;           /* Classified ADs btrieve file block ptr     */

struct usracc usr;            /* user account record                       */

struct sysvbl sv;             /* sys-variables record instance for updates */

#define ATPCSIZ 65                 /* maximum ad topic length              */

struct csiad {                     /* classified ad btrieve record layout  */
     LONG adno;                    /*   ad number from csitot              */
     CHAR topic[ATPCSIZ];          /*   ad topic                           */
     CHAR advrtr[UIDSIZ];          /*   advertiser's user-id               */
     INT crdate;                   /*   ad creation date                   */
     CHAR spare[88-82];            /*   spare space; decrease when adding  */
     CHAR adtext[1];               /*   ad text block (vbl length)         */
};

struct adstage {
     struct csiad ad;              /* ad staging area                      */
     CHAR text[16256];
} adstage;

#define BTVSEG      16
#define BTVALT      32

#define FILSIZ      100
#define MAXBSZ      (16+(24*16)+265+80)
#define MAXRLN      0x7FFF

INT exicod=0;                                                 /* exit code */
INT lstostat;                      /* last ropnbtv() call Btrieve status   */

struct filspc {                    /* Btrieve STAT command file spec       */
     SHORT reclen;
     SHORT pagsiz;
     SHORT numofx;
     LONG numofr;
     SHORT flags;
     SHORT reserved;
     SHORT unupag;
};

struct keyspc {                    /* Btrieve STAT command key specs       */
     SHORT keypos;
     SHORT keylen;
     SHORT flags;
     LONG numofk;
     CHAR extype;
     CHAR nullvalue;
     LONG reserved;
};
#define ANOSEG  0x10          /* keyspc.flags bit for key has another seg  */

struct statbf {               /* STAT command ret buf layout               */
     struct filspc fs;
     struct keyspc ks[SEGMAX];
     CHAR altcol[265];
};

#ifdef GCWINNT
struct flddef filspcFDA[] = {
     {CVTFLD_SHORT  ,3   ,fldoff(filspc,reclen)   ,NULL },
     {CVTFLD_LONG   ,1   ,fldoff(filspc,numofr)   ,NULL },
     {CVTFLD_SHORT  ,3   ,fldoff(filspc,flags)    ,NULL },
     {CVTFLD_END    ,0   ,0                       ,NULL }
};

struct flddef keyspcFDA[] = {
     {CVTFLD_SHORT  ,3   ,fldoff(keyspc,keypos)   ,NULL },
     {CVTFLD_LONG   ,1   ,fldoff(keyspc,numofk)   ,NULL },
     {CVTFLD_CHAR   ,2   ,fldoff(keyspc,extype)   ,NULL },
     {CVTFLD_LONG   ,1   ,fldoff(keyspc,reserved) ,NULL },
     {CVTFLD_END    ,0   ,0                       ,NULL }
};

struct flddef statbfFDA[] = {
     {CVTFLD_STRUCT ,1        ,fldoff(statbf,fs)       ,filspcFDA },
     {CVTFLD_STRUCT ,SEGMAX   ,fldoff(statbf,ks)       ,keyspcFDA },
     {CVTFLD_CHAR   ,265      ,fldoff(statbf,altcol)   ,NULL      },
     {CVTFLD_END    ,0        ,0                       ,NULL      }
};
#endif // GCWINNT

CHAR oldfil[GCMAXPTH];             /* file name currently being recovered  */
CHAR newfil[GCMAXPTH];             /* tmp file name for recovered file     */
CHAR virfil[GCMAXPTH];             /* virgin file name for current "oldfil"*/

static struct dfablk *bb;          /* current btvu file pointer set        */
static INT status;                 /* status code returned by btvu()       */
static INT bbomode=PRIMBV;         /* opnbtv() file-open mode              */
static USHORT lastlen;             /* length of last record read in        */

struct btvdat {   /* btrieve parameter block structure for use with INT 0x7B */
     CHAR *datbuf;
     INT dbflen;
     CHAR *posp38;
     CHAR *posblk;
     INT funcno;
     CHAR *key;
     CHAR keylen;
     CHAR keyno;
     INT *statpt;
     INT magic;
};

static INT rclckln(VOID) ;
static INT rbtvu(INT funcno,VOID *datbuf,VOID *key,INT keyno,INT rlen);

VOID updsv(VOID);
VOID upclas(VOID);
static INT rsttbtv(CHAR *statbuf,INT bufsize);
static INT rclsbtv(struct dfablk *bbp);
static INT rcrtbtv(CHAR *filnam,CHAR *databuf,INT lendbuf,INT keyno);
static VOID rsetbtv(struct dfablk *bbptr);
static INT rstpbtv(CHAR *recptr,INT stpopt);
static INT rinvbtv(CHAR *recptr,INT length);
static DFAFILE *ropnbtv(CHAR *filnam,INT maxlen);
static INT scncat(VOID) ;
static VOID rcvone(VOID) ;
static INT clone(CHAR *oldnam,CHAR *newnam);
static INT recover(CHAR *oldnam,CHAR *newnam);
static VOID pscnup(VOID) ;
static INT leave(VOID) ;

INT
main(                              /* main entry point                     */
INT argc,
CHAR *argv[])
{
TRY
     FILE *fp;
     INT onlycat=0;
     CHAR key[4];
     struct ffblk fb;

#ifdef GCWINNT
     if (!canRunUtil()) {
          MessageBox(NULL,NOPROCEED,
                    argv[0],MB_ICONSTOP|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
          return(1);
     }
#endif // GCWINNT
     initvid();
     cursiz(0);
     scn2mem(dosscn,0,GVIDSCNSIZ);
     if (sameas(argv[argc-1],"CATASTRO")) {
          argc--;
          onlycat=1;
     }
     if (argc > 1 && fnd1st(&fb,spr("%s.DAT",argv[1]),0)) {
          pscnup();
          do {
               strcpy(oldfil,fb.ff_name);
               oldfil[strlen(oldfil)-4]='\0';
               strcpy(newfil,oldfil);
               strcpy(virfil,GCVIRGIN);
               strcat(virfil,oldfil);
               strcat(oldfil,".DAT");
               strcat(newfil,".D$$");
               strcat(virfil,".VIR");
               rcvone();
          } while (fndnxt(&fb));
          if (fnd1st(&fb,"*.D$$",0)) {
               do {
                    strcpy(newfil,fb.ff_name);
                    newfil[strlen(newfil)-4]='\0';
                    strcat(newfil,".DAT");
                    rename(fb.ff_name,newfil);
               } while (fndnxt(&fb));
          }
     }
     while (scncat()) {
          pscnup();
          rcvone();
          rename(newfil,oldfil);
     }
     if (!onlycat && (fp=fopen("galcsiad.dat",FOPRB)) != NULL) {
          fclose(fp);
          pscnup();
          adbb=dfaOpen("galcsiad.dat",sizeof(struct csiad)+16256,NULL);
          svbb=dfaOpen("wgsvbl2.dat",sizeof(struct sysvbl),NULL);
          stzcpy(key,"key",4);
          dfaGetEQ(&sv,key,0);
          upclas();
          dfaClose(adbb);
          updsv();
          dfaClose(svbb);
     }
     return(leave());
EXCEPT
#ifdef GCWINNT
     return(1);
#endif
}

static VOID
pscnup(VOID)                        /* put up the FIXUP screen              */
{
     if (!scnup) {
          monorcol();
          cvtscn(scntbl[0]);
          explode(scntbl[0],13,11,66,24);
          setwin(0,15,12,64,23,1);
          locate(15,13);
          setatr(0x30);
          scnup=1;
     }
}

static INT
leave(VOID)                         /* leave the FIXUP utility              */
{
     setatr(0x07);
     printf(" ");
     locate(0,24);
     mem2scn(dosscn,0,GVIDSCNSIZ);
     cursiz(1);
     return(exicod);
}

VOID
updsv(VOID)
{
     CHAR key[4];

     dfaSetBlk(svbb);
     stzcpy(key,"key",4);
     dfaGetEQ(NULL,key,0);
     dfaUpdate(&sv);
}

VOID
upclas(VOID)
{
     LONG tot=0L;

     printf("Recalculating Classified Ad counts...\n");
     dfaSetBlk(adbb);
     if (dfaQueryLO(0)) {
          dfaAbsRec(NULL,0);
          do {
               movmem(adbb->data,&adstage,sizeof(struct csiad)+16256);
               if (adstage.ad.adno > tot) {
                    tot=adstage.ad.adno;
               }
          } while (dfaQueryNX());
          tot++;
     }
     sv.msgtot=max(tot,sv.msgtot);
}

static INT
scncat(VOID)                        /* scan GALCAT.OUT for errors to fix   */
{
     LONG curpos,endpos;
     static FILE *catfp=NULL;
     CHAR tmpbuf[512],*errptr,*btrptr;
     INT accerr;

     if (catfp == NULL) {
          if ((catfp=fopen("galcat.out",FOPRWA)) == NULL) {
               return(0);
          }
     }
     curpos=ftell(catfp);
     while (mdfgets(tmpbuf,512,catfp) != NULL) {
          if ((btrptr=strstr(tmpbuf,"BTRIEVE")) != NULL
              && (errptr=strstr(tmpbuf,"ERROR")) != NULL) {
               sscanf(errptr,"ERROR %d ON FILE \"%s",&errno,oldfil);
               accerr=(errno == 42);
               if (isascii(oldfil[0]) && (errno == 2 || errno == 14 ||
                   errno == 15 || errno == 19 || accerr)) {
                    movmem("(FIXED)",btrptr,7);
                    endpos=ftell(catfp);
                    fseek(catfp,curpos,SEEK_SET);
                    fprintf(catfp,tmpbuf);
                    fseek(catfp,endpos,SEEK_SET);
                    oldfil[strlen(oldfil)-5]='\0';
                    strupr(oldfil);
                    if (sameas(oldfil,"GALFILK2")) {
                         exicod=10;
                    }
                    else {
                         strcpy(newfil,oldfil);
                         strcat(newfil,".D$$");
                         strcpy(virfil,GCVIRGIN);
                         strcat(virfil,oldfil);
                         strcat(virfil,".VIR");
                         strcat(oldfil,".DAT");
                         return(1);
                    }
               }
          }
          curpos=ftell(catfp);
     }
     fclose(catfp);
     catfp=NULL;
     return(0);
}

static VOID
rcvone(VOID)                        /* recover a file (using global files)  */
{
     FILE *fp;

     lstostat=0;
     cntdir(oldfil);
     if ((fp=fopen(oldfil,FOPRA)) == NULL) {
          printf("Can't recover %s (file not found).\n\n",oldfil);
     }
     else if (dskfre(".") < (numbyts/1024L)) {
          fclose(fp);
          printf("Can't recover %s (not enough disk space).\n\n",oldfil);
     }
     else {
          fclose(fp);
          printf("Recovering %12s...  ",oldfil);
          if ((!clone(virfil,newfil) && !clone(oldfil,newfil)) ||
              !recover(oldfil,newfil)) {
               unlink(newfil);
               printf(" can't recover.\n");
               if (lstostat == 51) {    /* Btrieve file has owner          */
                    printf("File has owner set.\n\n");
               }
               else {
                    if ((fp=fopen(virfil,FOPRA)) != NULL) {
                         fclose(fp);
                         unlink(oldfil);
                         printf("Replacing with original %s...\n\n",oldfil);
                         system(spr("COPY %s %s > NUL",virfil,newfil));
                    }
                    else {
                         printf("Original not found.  Replace with back-up.\n\n");
                    }
               }
          }
          else {
               unlink(oldfil);
               printf("\n\n");
          }
     }
}

static INT
clone(                             /* clone (copy and 0) BTRIEVE data file */
CHAR *oldnam,                      /*   old file name                      */
CHAR *newnam)                      /*   new file name                      */
{
     DFAFILE *oldbb;
     INT addalt=0,keynum;
     INT i,databuflen,nkeys;
     CHAR keybuf[100];
     struct statbf statbuf;
     CHAR tmpbuf[sizeof statbuf];

     memset(&tmpbuf,0,sizeof(tmpbuf));
     if ((oldbb=ropnbtv(oldnam,MAXBSZ)) == NULL
      || !rsttbtv(tmpbuf,sizeof(tmpbuf))) {
          return(0);
     }
#ifdef GCWINNT
     cvtData(tmpbuf,&statbuf,sizeof(statbuf),statbfFDA,
                                             CVTDFA,CVTSERVER,CHAN_NUL);
#else
     movmem(tmpbuf,&statbuf,sizeof(statbuf));
#endif // GCWINNT
     nkeys=statbuf.fs.numofx;
     for (i=0 ; i < nkeys ; i++) {
          if (statbuf.ks[i].flags&BTVALT) {
               addalt=1;
          }
          if (statbuf.ks[i].flags&BTVSEG) {
               nkeys++;
          }
     }
     databuflen=16+(nkeys*16)+(addalt*265);
     statbuf.fs.numofr=0L;
     statbuf.fs.reserved=0;
#ifdef GCWINNT
     cvtData(&statbuf.fs,tmpbuf,sizeof(struct filspc),filspcFDA,
             CVTSERVER,CVTDFA,CHAN_NUL);
#else
     movmem(&statbuf.fs,tmpbuf,sizeof(struct filspc));
#endif // GCWINNT
     for (i=0 ; i < nkeys ; i++) {
          statbuf.ks[i].numofk=0L;
          statbuf.ks[i].nullvalue=0;
          statbuf.ks[i].reserved=0L;
#ifdef GCWINNT
          cvtData(&statbuf.ks[i],tmpbuf+(16*(i+1)),sizeof(struct keyspc),
                  keyspcFDA,CVTSERVER,CVTDFA,CHAN_NUL);
#else
          movmem(&statbuf.ks[i],tmpbuf+(16*(i+1)),sizeof(struct keyspc));
#endif // GCWINNT
     }
     keynum=0;
     strcpy(keybuf,newnam);
     if (!rclsbtv(oldbb) || !rcrtbtv(keybuf,tmpbuf,databuflen,keynum)) {
          return(0);
     }
     return(1);
}

static INT
recover(oldnam,newnam)             /* recover contents of 1 .DAT -> another*/
CHAR *oldnam,*newnam;                   /* old file name and new file name */
{
     INT ret;
     INT reclen;
     ULONG count=1;
     DFAFILE *oldbb,*newbb;

     bbomode=RONLBV;
     if ((oldbb=ropnbtv(oldnam,MAXRLN)) == NULL) {
          bbomode=PRIMBV;
          return(0);
     }
     bbomode=ACCLBV;
     if ((newbb=ropnbtv(newnam,MAXRLN)) == NULL) {
          rclsbtv(oldbb);
          bbomode=PRIMBV;
          return(0);
     }
     bbomode=PRIMBV;
     rsetbtv(oldbb);
     if ((ret=rstpbtv(NULL,33)) == 1) {
          printf("Record #          ");
          do {
               printf("\b\b\b\b\b\b\b\b\b\b%-10s",l2as(count++));
               rsetbtv(newbb);
               reclen=lastlen;
               rinvbtv(oldbb->data,reclen);
               rsetbtv(oldbb);
          } while ((ret=rstpbtv(NULL,24)) == 1);
     }
     rclsbtv(newbb);
     rclsbtv(oldbb);
     if (ret == -1) {
          return(0);
     }
     return(1);
}

static DFAFILE *
ropnbtv(                                /* open a Btrieve file; return ptr.*/
CHAR *filnam,                           /* name of file to open            */
INT maxlen)                             /* maximum length of recs in file  */
{
     INT stat;
     INT len;

     bb=(struct dfablk *)alcmem(sizeof(struct dfablk));
     bb->filnam=alcmem(strlen(filnam)+1);
     strcpy(bb->filnam,filnam);
     bb->reclen=maxlen;
     bb->data=alcmem(maxlen);
     if ((stat=rbtvu(0,NULL,filnam,bbomode,bb->reclen)) != 0) {
          if (lstostat == 0) {
               lstostat=stat;
          }
          return(NULL);
     }
     if ((len=rclckln()) == -1) {
          return(NULL);
     }
     bb->key=alcmem(len);
     return(bb);
}

static INT
rsttbtv(                      /* Get btrieve file status-offline use only  */
CHAR *statbuf,                /* status buffer to fill in                  */
INT bufsize)                  /* status buffer size                        */
{
     CHAR kbf[64];

     if (rbtvu(15,statbuf,kbf,0,bufsize) != 0) {
          return(0);
     }
     return(1);
}

static INT
rclsbtv(                      /* close specified Btrieve file              */
struct dfablk *bbp)           /* ptr to btrieve block for file to close    */
{
     if ((bb=bbp) != NULL && bb->filnam != NULL) {
          if (rbtvu(1,NULL,NULL,0,0) != 0) {
               return(0);
          }
          free(bb->key);
          free(bb->data);
          free(bb->filnam);
          bb->filnam=NULL;
          free(bb);
     }
     return(1);
}

static INT
rcrtbtv(                                /* Create a btrieve file           */
CHAR *filnam,                           /* name of file to be created      */
CHAR *databuf,                          /* buffer holding creation parms   */
INT lendbuf,                            /* length of data buffer           */
INT keyno)                              /* parameter used in creating file */
{
     static DFAFILE *crtbb=NULL;

     if (crtbb == NULL) {
          crtbb=alcmem(sizeof(DFAFILE));
     }
     setmem(crtbb,sizeof(DFAFILE),0);
     crtbb->key=filnam;
     crtbb->data=databuf;
     crtbb->reclen=lendbuf;
     rsetbtv(crtbb);
     if (rbtvu(14,crtbb->data,crtbb->key,keyno,crtbb->reclen) != 0) {
          crtbb->filnam=filnam;
          return(0);
     }
     return(1);
}

static VOID
rsetbtv(                      /* set a Btrieve data block for use          */
struct dfablk *bbptr)         /* btrieve pointer to make current           */
{
     bb=bbptr;
}

static INT
rstpbtv(                      /* "step" based btrieve operations           */
CHAR *recptr,                 /* ptr to area to retrieve record into       */
INT stpopt)                   /* step option to be performed               */
{
     if (recptr == NULL) {
          recptr=bb->data;
     }
     if (rbtvu(stpopt,recptr,NULL,0,bb->reclen) != 0) {
          if (status == 9) {
               return(0);
          }
          else {
               return(-1);
          }
     }
     return(1);
}

static INT
rinvbtv(                      /* insert new variable length Btrieve record */
CHAR *recptr,                 /* ptr to area to that for record insert     */
INT length)                   /* length of record to be inserted           */
{
     if (recptr == NULL) {
          recptr=bb->data;
     }
     if (rbtvu(2,recptr,bb->key,0,length) != 0) {
          return(0);
     }
     return(1);
}

static INT
rclckln(VOID)                 /* calculate all key lengths in curr file    */
{                             /*   (and return the max of them all)        */
     CHAR kbf[64];
     INT i,keyno,klen,maxofa;
     struct statbf tmpbuf;

     if (rbtvu(15,&tmpbuf,kbf,0,sizeof(struct statbf)) != 0) {
          return(-1);
     }
#ifdef GCWINNT
     cvtDataIP(&tmpbuf,sizeof(struct statbf),sizeof(struct statbf),statbfFDA,
               CVTDFA,CVTSERVER,CHAN_NUL);
#endif // GCWINNT
     maxofa=0;
     for (i=0,keyno=0 ; keyno < tmpbuf.fs.numofx ; i++,keyno++) {
          klen=tmpbuf.ks[i].keylen;
          while (tmpbuf.ks[i].flags&ANOSEG) {
               klen+=tmpbuf.ks[++i].keylen;
          }
          bb->keylns[keyno]=klen;
          if (klen > maxofa) {
               maxofa=klen;
          }
     }
     return(maxofa+1);
}

static INT
rbtvu(                             /* underlying Btrieve command interface */
INT funcno,                        /* btrieve function number to execute   */
VOID *datbuf,                      /* ptr to data buffer                   */
VOID *key,                         /* ptr to key buffer                    */
INT keyno,                         /* key number to work with              */
INT rlen)                          /* record length                        */
{
#ifdef GCWINNT
     extern SHORT __stdcall __import BTRCALL(USHORT operation,
                                             VOID *posBlock,
                                             VOID *dataBuffer,
                                             ULONG *dataLength,
                                             VOID *keyBuffer,
                                             CHAR keyLength,
                                             CHAR ckeynum);

     status=BTRCALL((USHORT)funcno,
                    (VOID *)bb->posblk,
                    datbuf,
                    (ULONG*)&rlen,
                    key,
                    (CHAR)255,
                    (CHAR)keyno);
#else
     union REGS regs;
     struct SREGS sregs;
     struct btvdat btvdat;
     static INT btrvup=0,alrcat=0;

     if (!btrvup) {
          regs.x.ax=0x357B;
          int86x(0x21,&regs,&regs,&sregs);
          if (regs.x.bx != 0x33) {
               if (alrcat) {
                    return(0);
               }
               alrcat=1;
               catastro("Run BTRIEVE /P:2048 first, before running WGSRIDX!");
          }
          btrvup=1;
     }
     btvdat.datbuf=datbuf;
     btvdat.dbflen=rlen;
     btvdat.posp38=((CHAR *)bb->posblk)+38;
     btvdat.posblk=(CHAR *)bb->posblk;
     btvdat.funcno=funcno;
     btvdat.key=key;
     btvdat.keylen=(CHAR)255;
     btvdat.keyno=(CHAR)keyno;
     *(btvdat.statpt=&status)=0;
     btvdat.magic=24950;
     regs.x.dx=(short)&btvdat;
     segread(&sregs);
     sregs.ds=sregs.ss;
     int86x(0x7B,&regs,&regs,&sregs);
#endif                             // GCWINNT
     if (status == 22) {                     /* truncate buffer overflows  */
          *((CHAR *)datbuf+bb->reclen-1)='\0';
          status=0;
     }
#ifdef GCWINNT
     lastlen=rlen;
#else
     lastlen=btvdat.dbflen;
#endif                             // GCWINNT
     return(status);
}
