/***************************************************************************
 *                                                                         *
 *   GCWINVER.CPP                                                          *
 *                                                                         *
 *   Copyright (c) 1995-1996 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   These routines perform Windows version control on files.              *
 *                                                                         *
 *                                            - D. Pitchford 12/26/95      *
 *                                                                         *
 ***************************************************************************/

#include "stdafx.h"

struct flddef dpkwvFDA[] = {
     { CVTFLD_LONG ,2       ,fldoff(dpkwv,msl)   ,NULL },
     { CVTFLD_SHORT,2       ,fldoff(dpkwv,date)  ,NULL },
     { CVTFLD_LONG ,1       ,fldoff(dpkwv,size)  ,NULL },
     { CVTFLD_CHAR ,GCMAXPTH,fldoff(dpkwv,filnam),NULL },
     { CVTFLD_END  ,0        ,0                  ,NULL }
};

#define VERBUF 16384               // size of file chunk for getwinver()   

static struct dpkwv *
cachefnd(                          // find info in cache                   
struct dpkwv *info);

static VOID
cacheinf(                          // store info in cache                  
struct dpkwv *info);

static
struct cachestr {
     struct dpkwv dpkwv;
     struct cachestr *next;
} *head=NULL;

INT                                //   returns 0 if files are same version
winvertest(                        // compare Windows versions of 2 files  
CHAR *f1,                          //   file 1 - returns -1 if newer       
CHAR *f2)                          //   file 2 - returns 1 if newer        
{
     struct dpkwv v1;
     struct dpkwv v2;

     stzcpy(v1.filnam,f1,GCMAXPTH);
     if (!getwinver(&v1)) {
          return(1);
     }
     stzcpy(v2.filnam,f2,GCMAXPTH);
     if (!getwinver(&v2)) {
          return(-1);
     }
     return(wvcompare(&v1,&v2));
}

INT                                //   rtns -1, 0, 1 (newer, same, older) 
wvcompare(                         // compare Windows file versions        
struct dpkwv *v1,                  //   info for file 1  (-1 if this newer)
struct dpkwv *v2)                  //   info for file 2  (1 if this newer) 
{
     UINT a1,a2;

     if (v1->msl > v2->msl) {
          return(-1);
     }
     if (v1->msl < v2->msl) {
          return(1);
     }
     if (v1->lsl > v2->lsl) {
          return(-1);
     }
     if (v1->lsl < v2->lsl) {
          return(1);
     }
     if (v1->msl != 0L || v1->lsl != 0L) {
          if (v2->msl != 0L || v2->lsl != 0L) {
               return(0);
          }
          return(-1);
     }
     if (v2->msl != 0L || v2->lsl != 0L) {
          return(1);
     }
     a1=cofdat(v1->date);
     a2=cofdat(v2->date);
     if (a1 > a2) {
          return(-1);
     }
     if (a1 < a2) {
          return(1);
     }
     if (v1->time > v2->time) {
          return(-1);
     }
     if (v1->time < v2->time) {
          return(1);
     }
     return(0);
}

GBOOL                              //   returns FALSE if no file           
getwinver(                         // read Windows file version            
struct dpkwv *info)                //   version info (needs filnam)        
{
     Cffblk fb;
     FILE *fp;
     CHAR *buf,*s;
     INT i,sz;
     LONG sk;
     struct dpkwv *cache;

     if (!fnd1st(&fb,info->filnam,0)) {
          return(FALSE);
     }
     if ((cache=cachefnd(info)) != NULL) {
          if (fb.ff_fdate == cache->date
           && fb.ff_ftime == cache->time
           && (ULONG)fb.ff_fsize == cache->size) {
               *info=*cache;
               return(TRUE);
          }
     }
     if ((fp=fopen(info->filnam,FOPRB)) == NULL) {
          return(FALSE);
     }
     info->date=fb.ff_fdate;
     info->time=fb.ff_ftime;
     info->size=fb.ff_fsize;
     info->msl=info->lsl=0L;
     buf=(CHAR *)alczer(VERBUF+1);
     sz=VERBUF/2;
     sk=fb.ff_fsize;
     do {
          sk-=VERBUF/2;
          if (sk < 0L) {
               sz+=(INT)sk;
               sk=0L;
          }
          memmove(buf+sz,buf,sz);
          fseek(fp,sk,SEEK_SET);
          if (fread(buf,sz,1,fp) != 1) {
               break; // breaks do {} while(); 
          }
          for (s=buf,i=0 ; i < VERBUF-32 ; s++,i++) {
               if (sameto("VS_VERSION_INFO",s)) {
                    if (ulngval((VOID *)(s+16)) == 0xFEEF04BDL) {
                         info->msl=ulngval((VOID *)(s+24));
                         info->lsl=ulngval((VOID *)(s+28));
                    }
                    sk=0L;    // indicating "i'm done" 
                    break;    // breaks for() {} 
               }
          }
     } while (sk > 0L);
     free(buf);
     fclose(fp);
     cacheinf(info);
     return(TRUE);
}

static struct dpkwv *
cachefnd(                          // find info in cache                   
struct dpkwv *info)
{
     struct cachestr *cur;

     for (cur=head ; cur != NULL ; cur=cur->next) {
          if (sameas(info->filnam,cur->dpkwv.filnam)) {
               return(&cur->dpkwv);
          }
     }
     return(NULL);
}

static void
cacheinf(                          // store info in cache                  
struct dpkwv *info)
{
     struct dpkwv *cache;
     struct cachestr *current;

     if ((cache=cachefnd(info)) != NULL) {
          *cache=*info;
     }
     else {
          current=(struct cachestr *)alczer(sizeof(struct cachestr));
          current->dpkwv=*info;
          current->next=head;
          head=current;
     }
}