/*
 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 "global.h"
#ifdef QRZCALLB
#include "ctype.h"

#define RECLEN 200
#include "mbuf.h"
#include "socket.h"
#ifndef MSDOS
#include "session.h"
#else
#include "hardware.h"
#endif

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: qrz.c,v 1.15 1997/07/31 00:44:20 root Exp $";
#endif

int cb_lookup (int s,char *,FILE *);

/* does the actual lookup.   */
static int  qrzfind(char *,int s, FILE *);
/* Parse QRZ data record.    */
static void parse_record(char *, int s);
/* Format QRZ Database Date. */
static void formatdate(char *);
/* Scan a QRZ record for the next field */
static char *scan_field(char *cp, char fieldbuf[], int fieldlen);

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

/*     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 */
#ifndef _lint
  char  version[8];      /* Database Version ID              */
#endif
} index_header;

/*
 *     Old Index Header Block Definition
 */
typedef struct {
#ifndef _lint
  char  dataname[13];    /* Name of the data file            */
#endif
  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 {
      usprintf(s,"\n   No data found for %s.\n", prettycall);
      return 1;
   }
}

/*lint -esym(550,numkeys) */
static int qrzfind(char *call_in, int s, FILE *fpout OPTIONAL)
{
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;
int          workbufsize;     /* bytesperkey + fudge */
long         fpos;
long         frc;
char         temp[255];
char         call[8];


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

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


   k = (int) 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 )                                      */
   /*                                                                   */
   call[0] = call_in[0];
   i = j = 1;
   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 position 2 or 3. */
      usprintf(s,"Error parsing callsign... %s\n", call_in);
      return 0;
   }
   for ( ; j < (int)strlen(call_in); j++)
      call[i++] = call_in[j];
   call[6]=0;
   (void) strupr(call);


   /* Get path info. Use defaults if not available. */
   if (qrzdir == NULLCHAR) {
      if((qrzdir = getenv("QRZPATH")) == NULLCHAR) {
         #ifdef UNIX
         qrzdir =  strdup("/callbk");
         #else
         qrzdir =  strdup("\\callbk");
         #endif
      }
   } /* endif */
   if (qrzdrv == NULLCHAR) {
      if((qrzdrv = getenv("QRZDRV")) == NULLCHAR) {
         #ifdef UNIX
         qrzdrv =  strdup("/nos");
         #else
         qrzdrv =  strdup("D:");
         #endif
      }
   } /* endif */

   /* 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.                                                */
   /*                                                                   */
   #ifdef UNIX
   sprintf(temp,"%s%s/%s",qrzdrv,qrzdir,IndexFile);
   #else
   sprintf(temp,"%s%s\\%s",qrzdrv,qrzdir,IndexFile);
   #endif
   if((fp = fopen(temp,"rt"))==NULLFILE) {
      usprintf(s,"Error opening Index: %s\n",temp);
      return 0;
   }

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

   /* Old Style Index has a '0' at pos 16 and 17. - this is SUPPOSE to be valid !?!? */
   if((int)idxhdr.dataname[16] != 0) {		/*lint !e415 */
      /* 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 = (int) fread(&oldidxhdr,sizeof(oldidxhdr),1,fp);
      if(size != 1) {
         usprintf(s,"Error reading Index Header.\n");
         (void) fclose(fp);
         return 0;
      }
      bytesperkey = (int)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.  (We fudge bytesperkey by RECLEN to avoid a truncated */
   /* last record in the buffer).                                       */
   /*                                                                   */
   workbufsize = bytesperkey+RECLEN;
   bufptr      = mallocw((unsigned) workbufsize);           /* Get space for buffer */
   if (bufptr == NULL) {     /* no space */
      usprintf(s,"No space for %d byte buffer.\n",workbufsize);
      (void) fclose(fp);
      return(0);
   }

   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 = (int) fread(buf,(unsigned) keylen, (unsigned) 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, (unsigned) keylen);
        temp[keylen] = 0;
        if(strncmp(&temp[3],&call[3],3) >= 0) {
           if(strncmp(&temp[3],&call[3],3) > 0) {
              found = 1;
              slotcnt--;
           }
           else {  /* last 3 equal. How about digit? */
              if(strncmp(&temp[2],&call[2],1) >= 0) {
                 if(strncmp(&temp[2],&call[2],1) > 0) {
                    slotcnt--;
                    found = 1;
                 }
                 else /* digits are equal. Check first 2 chars */
                    if(strncmp(&temp[0],&call[0],2) > 0) {
                       slotcnt--;
                       found = 1;
                    }
              }
           }
        }
        if(found)
           break;
        buf += keylen;
      }
   } while(!found); /* enddo */

   slotcnt--;

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

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

      /* seek to correct posistion in file .                           */
      fpos = (long) bytesperkey*slotcnt;

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

      /* Read slot from callbk file. */
      size = (int) fread(buf, (unsigned) workbufsize,1,fp);
      if(size < 1) {
         usprintf(s,"Error reading Database file.\n");
         free(bufptr);
         (void) fclose(fp);
         return 0;
      }

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

      i = 0;
      for(;;) {
         if(i>workbufsize) {
            found = 0;
            break;
         }
         k = (int) strcspn(buf,"\n");
         if(k == 0) {
            found = 0;
            break;
         }
         strncpy(temp,buf, (unsigned) k);
         temp[k] = 0;
         for(j=0;j<=k;j++)
            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);
   return found;
}

static char *
scan_field(char *cp, char fieldbuf[], int fieldlen)
{
  char *cp1;

  cp1 = strchr(cp, ',');
  if (cp1) {
     *cp1++ = 0;
     strncpy(fieldbuf, cp, (unsigned) fieldlen);
     fieldbuf[fieldlen-1] = 0;
     cp=cp1;
  }
  else fieldbuf[0]=0;
  return(cp);
}

static 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 temp2[255];

int  rc;

#ifdef QRZDEBUG
  usprintf(s, "debug: %s\n", record);
#endif
  strcpy(temp2,record);
  cp=temp2;
  cp = scan_field(cp, callsign, sizeof(callsign));

  if(strlen(record) < 16) {
     usprintf(s,"%s is an old call for %s\n", prettycall, cp);
     rc =  cb_lookup(s,cp,(FILE *) 0);
     if(rc != 0)
        usprintf(s,"no info found for %s.\n", cp);
     return;
  }

  cp = scan_field(cp, lastname, sizeof(lastname));

  cp = scan_field(cp, namesuffix, sizeof(namesuffix));

  cp = scan_field(cp, frstname, sizeof(frstname));

  cp = scan_field(cp, middleinit, sizeof(middleinit));

  cp = scan_field(cp, dateborn, sizeof(dateborn));

  cp = scan_field(cp, datelicensed, sizeof(datelicensed));

  cp = scan_field(cp, dateexpires, sizeof(dateexpires));

  cp = scan_field(cp, streetaddr, sizeof(streetaddr));

  cp = scan_field(cp, city, sizeof(city));

  cp = scan_field(cp, state, sizeof(state));

  cp = scan_field(cp, zipcode, sizeof(zipcode));

  cp = scan_field(cp, license_class, sizeof(license_class));

  cp = scan_field(cp, prevcall, sizeof(prevcall));

  cp = scan_field(cp, prevclass, sizeof(prevclass));

  sprintf(fullname,"%s%s%s%s%s%s%s",frstname, middleinit[0] == 0 ? "":" ",middleinit,
      middleinit[0] == 0 ? " ":". ", lastname, namesuffix[0] == 0 ? " ":", ", 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[0]?license_class:"r"), 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;
}

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

int  dayarray[13] = { 0, 31,  60,  91, 121, 152, 182,  /* 0 permits handling 00000 better */
                     213, 244, 274, 305, 335, 366 };

   strncpy(year,date,2);
   year[2]  = 0;
   years    = atoi(year);
#ifdef notdef
   if(years < 20)   /* fails for DOB, where 15 => 1915. -- n5knx */
      years += 2000;
   else
      years += 1900;
#endif

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

   if((years % 4) != 0)   /* fails in 2100 */
      if(days > 59)
         days++;

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