// June 15, 1994 - KF5MG - Jack Snodgrass
//
// This code was adapted/copied from the SAM.C code. The code to process
// the QRZ disk was taken from the QRZ CD-ROM. This code could work
// with the name and zipcode versions of the Callbook data, but as-is, it
// only processes callsigns.
//
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <io.h>
#include <fcntl.h>

#define reclen 160
#include "global.h"
#ifdef QRZCALLB
#include "mbuf.h"
#include "socket.h"
#include "session.h"
#include "proc.h"
#include "netuser.h"
#include "commands.h"
#include "tty.h"
#include "config.h"

extern char *Callserver;                                   /* buckbook.c */
int cb_lookup __ARGS((int s,char *,FILE *));

/* does the actual lookup.   */
int  qrzfind(char *,int s, FILE *);
/* Parse QRZ data record.    */
void parse_record(char *, int s);
/* Format QRZ Database Date. */
void formatdate(char *);

/* Gobal variables. */
char pretty_date[12];
char prettycall[7];
char *qrzdir;
char *qrzdrv;

/*     Taken from the QRZ CD-ROM disk.
 *     This block is located at the start of each index
 */
/*
 *     New Index Header Block Definition
 */
typedef struct {
  char  dataname[16];    /* Name of the data file            */
  char  bytesperkey[8];  /* Data Bytes per Index Item        */
  char  numkeys[8];      /* Number of items in this index    */
  char  keylen[8];       /* Length of each key item in bytes */
  char  version[8];      /* Database Version ID              */
} index_header;

/*
 *     Old Index Header Block Definition
 */
typedef struct {
  char  dataname[13];    /* Name of the data file            */
  long  bytesperkey;     /* Data Bytes per Index Item        */
  int   numkeys;         /* Number of items in this index    */
  int   keylen;          /* Length of each key item in bytes */
} old_index_header ;

/* return values - 2= Callbook Error 1= not found, 0= okay */
int cb_lookup(s, str, fp)
int s;
char *str;
FILE *fp;
{
   if(qrzfind(str,s,fp))
      return 0;
   else
      return 1;
}

int qrzfind(char *call_in, int s, FILE *fpout)
{
index_header     idxhdr;
old_index_header oldidxhdr;

FILE         *fp;
char         *buf;
char         *bufptr;
char         IndexFile[] = "callbkc.idx";
int          size;
int          bytesperkey;     /* Data Bytes per Index Item        */
int          numkeys;         /* Number of items in this index    */
int          keylen;          /* Length of each key item in bytes */
int          slots;
int          i,j,k,found,slotcnt;
long         fpos;
long         frc;
char         temp[255];
char         *cp;
char         *cp2;
char         call[8];


   /* call needs to be blank filled. */
   strcpy(call,"       ");

   /* Pretty call holds the original, un-qrz-formatted callsign. */
   strcpy(prettycall, call_in);
   strupr(prettycall);


   k = strlen(call_in);
   if (k>6) {
      usprintf(s,"Callsign too long.\n");
      return 0;
   }

   /* Callsigns in the QRZ Index are stored in a weird format. They are */
   /* 6 characters in length ( padded with spaces ), in the format of   */
   /* ccdccc where the digit is always in the 3rd posistion. If the     */
   /* callsign is a 1-by-something callsign, the 2nd posistion will be  */
   /* blank. KF5MG will be stored as KF5MGb. N5VGC will be stored as    */
   /* Nb5VGC. ( the b are spaces )                                      */
   /*                                                                   */
   i = 1;
   j = 1;
   call[0] = call_in[0];
   if(!isdigit(call_in[j]))
      call[i++] = call_in[j++];
   else
      call[i++] = ' ';
   if(isdigit(call_in[j])) {
      call[i++] = call_in[j++];
   } else {
      /* No digit found in posistion 2 or 3. */
      usprintf(s,"Error parsing callsign... %s\n", call_in);
      return 0;
   }
   if(isalpha(call_in[j]))
      call[i++] = call_in[j];
   else
      call[i++] = ' ';
   j++;
   if(isalpha(call_in[j]))
      call[i++] = call_in[j];
   else
      call[i++] = ' ';
   j++;
   if(isalpha(call_in[j]))
      call[i++] = call_in[j];
   else
      call[i++] = ' ';


   call[6]   = 0;
   strupr(call);


   qrzdir = strdup(getenv("QRZPATH"));
   qrzdrv = strdup(getenv("QRZDRV"));

   if(qrzdir == NULLCHAR)
      qrzdir =  strdup("\\callbk");

   if(qrzdrv == NULLCHAR)
      qrzdrv =  strdup("C:");

   /* Open the index file.  We'll use it to tell us the name of the     */
   /* database file. We'll also find the database version and some      */
   /* other useful info.                                                */
   /*                                                                   */
   sprintf(temp,"%s%s\\%s",qrzdrv,qrzdir,IndexFile);
   if((fp = fopen(temp,"rt"))==NULL) {
      usprintf(s,"Error opening Index: %s\n",temp);
      free(qrzdir);
      free(qrzdrv);
      return 0;
   }

   size = fread(&idxhdr,sizeof(idxhdr),1,fp);
   if(size != 1) {
      usprintf(s,"Error reading Index Header.\n");
      free(qrzdir);
      free(qrzdrv);
      return 0;
   }

   // Old Style Index has a '0' at pos 16 and 17.
   if((int)idxhdr.dataname[16] != 0) {
      // This is a 'new' style index
      bytesperkey = atoi(idxhdr.bytesperkey);   /* size of data area.   */
      numkeys     = atoi(idxhdr.numkeys);       /* # of keys in file.   */
      keylen      = atoi(idxhdr.keylen);        /* length of each key.  */
   } else {
      // This is an 'old' style index.
      // rewind the file and read the header using the old_index_header struct.
      rewind(fp);

      size = fread(&oldidxhdr,sizeof(oldidxhdr),1,fp);
      if(size != 1) {
         usprintf(s,"Error reading Index Header.\n");
         free(qrzdir);
         free(qrzdrv);
         return 0;
      }
      bytesperkey = oldidxhdr.bytesperkey;      /* size of data area.   */
      numkeys     = oldidxhdr.numkeys;          /* # of keys in file.   */
      keylen      = oldidxhdr.keylen;           /* length of each key.  */
   }

   /* This is a 10K ish buffer. Each 'key' in the index covers almost   */
   /* 10K of data. Once you find the correct key, you have to read the  */
   /* 10K chunk of data from the data file. You then start searching    */
   /* for the correct callsign. This code uses the same 10k buffer to   */
   /* scan the index file ( typically 40K so you might do 4 reads ) to  */
   /* save memory. Once you've found the correct key, you calculate the */
   /* offset into the database. You then read the data base into your   */
   /* 10K buffer.
   /*                                                                   */
   bufptr      = malloc(bytesperkey+400);       /* Get space for buffer */
   slots       = bytesperkey / keylen;          /* Calculate # of slots */
   found       = 0;
   slotcnt     = 0;

   do {
      /* Point floating buf pointer to start of big buffer.   */
      buf      = bufptr;
      /* Read slots number of entries that are keylen in size */
      size = fread(buf,keylen,slots,fp);
      if(size == 0) {
         usprintf(s,"Error reading Index file.\n");
         found = 2;
      }

      /* Start scanning Index buffer. If the data is less than your search */
      /* Value... keep going. If the Data is greater, then your done. You  */
      /* then subtract one from your slotcnt (unless it's an exact match)  */
      /* and that's the closet record to your data.                        */
      /*                                                                   */
      for(i=0;i<size;i++) {
        slotcnt++;
        strncpy(temp,(char *)buf,keylen);
        temp[keylen] = 0;
        if(strncmp(&temp[3],&call[3],3) >= 0) {
           if(strncmp(&temp[3],&call[3],3) > 0) {
           found = 1;
              slotcnt--;
           }
           else
              if(strncmp(&temp[2],&call[2],4) > 0) {
                 slotcnt--;
                 found = 1;
              }
        }
        if(found)
           break;
        /*
        *buf++;
        *buf++;
        *buf++;
        *buf++;
        *buf++;
        *buf++;
        */
        buf += 6;
      }
   } while(!found); /* enddo */

   slotcnt--;

   /* We're done with the Index so we can close it. */
   fclose(fp);

   /* If we found a match, the calculate the offset and read the data. */
   if(found == 1) {
      buf      = bufptr;
      sprintf(temp,"%s%s\\%s",qrzdrv,qrzdir,idxhdr.dataname);
      if((fp = fopen(temp,"rt"))==NULL) {
         usprintf(s,"Error opening Database: %s\n",temp);
         free(bufptr);
         free(qrzdir);
         free(qrzdrv);
         return 0;
      }

      /* Just to to be save, we read 200 bytes before and after so we  */
      /* don't miss anything.                                          */
      /* seek to correct posistion in file .                           */
      fpos = (long) bytesperkey*slotcnt;
      fpos -= 200;

      frc = lseek(fileno(fp),(long) fpos,SEEK_SET);
      if(frc < 1) {
         usprintf(s,"Error seeking Database file.\n");
         free(bufptr);
         free(qrzdir);
         free(qrzdrv);
         return 0;
      }

      /* Read slot from callbk file. */
      size = fread(buf,bytesperkey+400,1,fp);
      if(size < 1) {
         usprintf(s,"Error reading Database file.\n");
         free(bufptr);
         free(qrzdir);
         free(qrzdrv);
         return 0;
      }

      /* Done with data file. Now we can close it too. Our users is either */
      /* in our buffer or doesn't exist.                                   */
      fclose(fp);

      i = 0;
      for(;;) {
         if(i>bytesperkey+400)
            break;
         k = strcspn(buf,"\n");
         strncpy(temp,buf,k);
         temp[k] = 0;
         for(j=0;j<=k;j++)
            /*
            *buf++;
            */
            buf += 1;

         /* Find our user. */
         if(strncmp(temp,call,6) == 0) {
            /* Found it. Now read and format the data. */
            parse_record(temp,s);
            break;
         }
         i+=k;
      }
   }
   /* Done with the buffer. */
   free(bufptr);
   free(qrzdir);
   free(qrzdrv);
   return found;
}

void parse_record(char *record, int s) {
/*
 *    Standard Record Format
 */
char callsign[7];         /* Call Sign Decoded      */
char lastname[33];        /* Last Name              */
char namesuffix[3];       /* Name Suffix            */
char frstname[33];        /* First Name             */
char middleinit[3];       /* Middle Initial         */
char datelicensed[6];     /* Date Licensed mm/dd/yy */
char dateborn[6];         /* Date Born              */
char dateexpires[6];         /* Date Born              */
char streetaddr[33];      /* Street Address         */
char city[33];            /* City                   */
char state[3];            /* State Code             */
char zipcode[6];          /* Zip Code               */
char license_class[3];    /* License Class          */
char prevcall[7];         /* Previous Call          */
char prevclass[3];        /* Previous Class         */

char *cp;
char fullname[80];
char address[80];
char temp[255];
char temp2[255];
int  i,j,k;

  strcpy(temp2,record);
  cp = temp2;

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(callsign,temp);

  if(strlen(record) < 16) {
     usprintf(s,"%s is an old call for %s\n", prettycall, cp);
     return;
  }

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(lastname,temp);


  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(namesuffix,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(frstname,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(middleinit,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(dateborn,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(datelicensed, temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(dateexpires,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(streetaddr,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(city,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(state,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(zipcode,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(license_class,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(prevcall,temp);

  k = strcspn(cp,",");
  strncpy(temp,cp,k);
  temp[k] = 0;
  for(j=0;j<=k;j++)
     cp += 1;
  strcpy(prevclass,temp);

  sprintf(fullname,"%s %s. %s, %s",frstname, middleinit, lastname, namesuffix);
  sprintf(address, "%s, %s, %s", city, state, zipcode);

  usprintf(s,"\n%-8s", prettycall);
  usprintf(s,"%-45s", fullname);
  formatdate(dateborn);
  usprintf(s,"Born:    %s\n", pretty_date);

  usprintf(s,"%-8s%-45s","", streetaddr);
  formatdate(datelicensed);
  usprintf(s,"Class: %s %s\n", license_class, pretty_date);

  usprintf(s,"%-8s%-45s", "", address);
  usprintf(s,"Prev:  %s %s\n", prevclass, prevcall);

  formatdate(dateexpires);
  usprintf(s,"%52s Expir:   %s\n", "", pretty_date);

  return;
}

void formatdate(char *date){
char year[5];
char rest[4];
int  mon;
int  day;
int  days;
int  years;
int  i;

int  dayarray[12] = { 31,  60,  91, 121, 152, 182,
                     213, 244, 274, 305, 335, 366 };

   strncpy(year,date,2);
   year[2]  = 0;
   years    = atoi(year);
   if(years < 20)
      years += 2000;
   else
      years += 1900;

   strncpy(rest,&date[2],3);
   rest[3]  = 0;
   days     = atoi(rest);

   if((years % 4) != 0)
      if(days > 59)
         days++;

   for(i=0;i<12;i++){
      if(days <= dayarray[i]) {
         mon = i;
         day = days - dayarray[i-1];
         break;
      }
   }
   sprintf(pretty_date, "%02d/%02d/%04d", mon+1,day,years);
}
#endif
