/*
                  ------------------------------------
                  *** Little Black Book © 1987 Lcc ***
                  ------------------------------------

   This program is presented in the spirit of keeping shareware alive.

   You may freely copy and distribute, for non-commercial purposes, both
Little Black Book and the limited file handler library (bfile.lib) provided
that this documentation accompanies the programs and that no charge is made
for copying and distribution.

   If you find these programs useful please send your contribution to the
address below.  If you desire the full-blown bfile library on disk complete
with source code (bfile is written in 'C' and is not Amiga specific) -

                Please send the nominal fee of $30¹ to:

                          Craig Nelson
                          Log Cabin Computer
                          921 Arcola Ct PP
                          Granbury, TX  76048

 ¹ A bona fide job offer may be substituted for cash as this programmer is
   currently unemployed (817) 573-7304.


                       Notes, caveats and stuff
                      --------------------------

 * Because of special keyboard/mouse handling, runs only under WorkBench
   release 1.2 (this does not apply to bfile.lib) 

 * Set up for 80 column because that's my preference

 * If the display flashes it's either your imagination or the beginning or
   end of file has been reached

 * For the most part, either the keyboard or the mouse may be used
   interchangeably - you'll get the idea just by playing around

   <Right Mouse>     move pointer to the bottom of window

   <RETURN>          when the pointer is at the bottom of window enters
                     edit mode

   <CTRL><RETURN>    exits edit mode

   <Right AMIGA><Q>  restores a field

   <Right AMIGA><X>  clears a field

   <Right Cursor>    next record

   <Left Cursor>     previous record

   F1 - 'FND'        finds the first occurence > or = to entry typed
                     (the record key is upper case so 'johnson' = 'JOHNSON')

   F2 - 'SAV'        saves changes made to an existing record

   F3 - 'ADD'        adds a new record

   F4 - 'DEL'        deletes a record

                                 - - -
*/

#include <intuition/intuition.h>
#include <devices/input.h>

#define  BLUP  0
#define  WHTP  1
#define  BLKP  2
#define  YLWP  3

#define  MESSAGE    (1 << wdw->UserPort->mp_SigBit)
#define  IGNORE_MSG ((qual & 0x0200) || (code & 0x80)) /*  Key up, repeat */
#define  ABORT_MSG   (qual & 0x2008)                   /*  CTRL, RMB      */

#define  FND_KEY  0x50     /*  F1           */
#define  SAV_KEY  0x51     /*  F2           */
#define  ADD_KEY  0x52     /*  F3           */
#define  DEL_KEY  0x53     /*  F4           */
#define  EDT_KEY  0x44     /*  Return       */
#define  PRV_KEY  0x4F     /*  Left arrow   */
#define  NXT_KEY  0x4E     /*  Right arrow  */

#define  NAM_GAD     1
#define  ADR_GAD     2
#define  CIT_GAD     3
#define  STA_GAD     4
#define  ZIP_GAD     5
#define  PHN_GAD     6
#define  NOT_GAD     7
#define  FND_GAD     8
#define  SAV_GAD     9
#define  ADD_GAD    10
#define  DEL_GAD    11
#define  PRV_GAD    12
#define  NXT_GAD    13
#define  NEG_GAD    14
#define  POS_GAD    15

#define  STR_GAD     7     /*  End of string gadgets  */
#define  WDW_GAD    13     /*  End of normal gadgets  */

/*------------------------ Required by bfile.lib -------------------------*/

extern   int   BFILE_OK;
typedef  char  BFILE[100];

/*----------------------- Database file definition -----------------------*/

#define  DUP   1          /*  Duplicate records OK  */

typedef  char  BKEY[26];  /*  Record key  */

struct DataRec {
         char  Name[26],
               Addr[26],
               City[15],
               State[3],
               Zip[6],
               Phone[15],
               Note[81];
} BookRec, SaveRec;

   BFILE       BookDataFile;
   BFILE       BookIndexFile;
   BKEY        BookKey;
   int         BookRecNum;

/*---------------------- Special external functions ----------------------*/

extern struct MsgPort   *CreatePort();
extern struct IOStdReq  *CreateStdIO();
extern struct TextFont  *OpenFont();

/*----------------------------- Global data ------------------------------*/

struct IntuitionBase    *IntuitionBase;
struct GfxBase          *GfxBase;
struct Window           *wdw;
struct IntuiMessage     *msg;
struct RastPort         *rp;
struct MsgPort          *dev;
struct IOStdReq         *req;
struct TextFont         *font;

   ULONG    class;
   USHORT   code;
   USHORT   qual;
   APTR     iadr;
   USHORT   gid;

   short    find_flag;

   char     Undo[81];
   char     Note[26];
   char     used[8];

   char
      wdwttl[] = { " Little Black Book © 1987 Lcc " },
      scrttl[] = { "Little Black Book © 1987 Lcc (817) 573-7304  WB 1.2 80 col" };

struct TextAttr
   attr = { "topaz.font", TOPAZ_EIGHTY, FS_NORMAL, FPF_ROMFONT };

struct InputEvent
   epos = { NULL, IECLASS_POINTERPOS, 0, IECODE_NOBUTTON, 0,0,0,0 },
   ebut = { NULL, IECLASS_POINTERPOS, 0, IECODE_LBUTTON,  0,0,0,0 };

   USHORT   prv_pix[] = { 0xFE3F,0xF87F,0xC0FF,0x0000,0xC0FF,0xF87F,0xFE3F },
            nxt_pix[] = { 0xFC7F,0xFE1F,0xFF03,0x0000,0xFF03,0xFE1F,0xFC7F };

struct Image
   prv_img = { 4,0, 16,7,1, (USHORT *) &prv_pix, 0x2,0x0, NULL },
   nxt_img = { 4,0, 16,7,1, (USHORT *) &nxt_pix, 0x2,0x0, NULL };

   SHORT    c03_pts[] = { -3,-1,   28,-1,   28,10,  -3,10,  -3,-1 },
            c06_pts[] = { -3,-1,   52,-1,   52,10,  -3,10,  -3,-1 },
            c15_pts[] = { -3,-1,  124,-1,  124,10,  -3,10,  -3,-1 },
            c26_pts[] = { -3,-1,  212,-1,  212,10,  -3,10,  -3,-1 },
            sel_pts[] = { -3,-1,   52,-1,   52, 9,  -3, 9,  -3,-1 };

struct Border
   c03_brdr = { -1,-1, BLUP,BLKP,JAM1, 5,(SHORT *) &c03_pts, NULL },
   c06_brdr = { -1,-1, BLUP,BLKP,JAM1, 5,(SHORT *) &c06_pts, NULL },
   c15_brdr = { -1,-1, BLUP,BLKP,JAM1, 5,(SHORT *) &c15_pts, NULL },
   c26_brdr = { -1,-1, BLUP,BLKP,JAM1, 5,(SHORT *) &c26_pts, NULL },
   sel_brdr = { -1,-1, YLWP,BLKP,JAM1, 5,(SHORT *) &sel_pts, NULL },
   neg_brdr = { -1,-1, BLKP,BLKP,JAM1, 5,(SHORT *) &c03_pts, NULL };

struct StringInfo
   nam_info = { BookRec.Name , Undo, 0,26,0,0,0,0,0,0, NULL,0,NULL },
   adr_info = { BookRec.Addr , Undo, 0,26,0,0,0,0,0,0, NULL,0,NULL },
   cit_info = { BookRec.City , Undo, 0,15,0,0,0,0,0,0, NULL,0,NULL },
   sta_info = { BookRec.State, Undo, 0, 3,0,0,0,0,0,0, NULL,0,NULL },
   zip_info = { BookRec.Zip  , Undo, 0, 6,0,0,0,0,0,0, NULL,0,NULL },
   phn_info = { BookRec.Phone, Undo, 0,15,0,0,0,0,0,0, NULL,0,NULL },
   not_info = { BookRec.Note , Undo, 0,81,0,0,0,0,0,0, NULL,0,NULL };

struct IntuiText
   nam_text = { BLUP,BLKP,JAM1, -44, 0,&attr, "Name"           , NULL      },
   adr_text = { BLUP,BLKP,JAM1, -44, 0,&attr, "Addr"           , NULL      },
   cit_text = { BLUP,BLKP,JAM1, -44, 0,&attr, "City"           , NULL      },
   sta_text = { BLUP,BLKP,JAM1, -52, 0,&attr, "State"          , NULL      },
   zip_text = { BLUP,BLKP,JAM1, -44, 0,&attr, "Zip"            , NULL      },
   phn_text = { BLUP,BLKP,JAM1, -28, 0,&attr, "Ph"             , NULL      },
   not_text = { BLUP,BLKP,JAM1, -44, 0,&attr, "Note"           , NULL      },
   fnd_text = { BLUP,BLKP,JAM1,   0, 0,&attr, "F1=FND"         , NULL      },
   sav_text = { BLUP,BLKP,JAM1,   0, 0,&attr, "F2=SAV"         , NULL      },
   add_text = { BLUP,BLKP,JAM1,   0, 0,&attr, "F3=ADD"         , NULL      },
   del_text = { BLUP,BLKP,JAM1,   0, 0,&attr, "F4=DEL"         , NULL      },
   usd_text = { BLUP,BLKP,JAM2,   0, 0,&attr, used             , NULL      },

   neg_text = { BLKP,BLKP,JAM1,   4, 0,&attr, "No"             , NULL      },
   pos_text = { BLKP,BLKP,JAM1,   0, 0,&attr, "Yes"            , NULL      },
   avf_text = { BLKP,BLKP,JAM1,  48, 3,&attr, "Add Duplicate?" , NULL      },
   dvf_text = { BLKP,BLKP,JAM1,  48, 3,&attr, "Delete Record?" , NULL      },
   svf_text = { BLKP,BLKP,JAM1,  48, 3,&attr, "Changed - Save?", NULL      },

   not_disp = { WHTP,BLUP,JAM2,  56,71,&attr, Note             , NULL      },
   phn_disp = { WHTP,BLUP,JAM2, 144,57,&attr, BookRec.Phone    , &not_disp },
   zip_disp = { WHTP,BLUP,JAM2,  56,57,&attr, BookRec.Zip      , &phn_disp },
   sta_disp = { WHTP,BLUP,JAM2, 240,43,&attr, BookRec.State    , &zip_disp },
   cit_disp = { WHTP,BLUP,JAM2,  56,43,&attr, BookRec.City     , &sta_disp },
   adr_disp = { WHTP,BLUP,JAM2,  56,29,&attr, BookRec.Addr     , &cit_disp },
   nam_disp = { WHTP,BLUP,JAM2,  56,15,&attr, BookRec.Name     , &adr_disp };

struct Gadget
   nam_gdgt = { NULL, 56,15,208,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c26_brdr, NULL, &nam_text, 0, (APTR) &nam_info,
                NAM_GAD, NULL },

   adr_gdgt = { NULL, 56,29,208,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c26_brdr, NULL, &adr_text, 0, (APTR) &adr_info,
                ADR_GAD, NULL },

   cit_gdgt = { NULL, 56,43,120,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c15_brdr, NULL, &cit_text, 0, (APTR) &cit_info,
                CIT_GAD, NULL },

   sta_gdgt = { NULL, 240,43,24,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c03_brdr, NULL, &sta_text, 0, (APTR) &sta_info,
                STA_GAD, NULL },

   zip_gdgt = { NULL,  56,57,48,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c06_brdr, NULL, &zip_text, 0, (APTR) &zip_info,
                ZIP_GAD, NULL },

   phn_gdgt = { NULL,144,57,120,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c15_brdr, NULL, &phn_text, 0, (APTR) &phn_info,
                PHN_GAD, NULL },

   not_gdgt = { NULL, 56,71,208,8,  GADGHCOMP, RELVERIFY, STRGADGET,
                (APTR) &c26_brdr, NULL, &not_text, 0, (APTR) &not_info,
                NOT_GAD, NULL },

   fnd_gdgt = { NULL,  15,87,48,7,  GADGHCOMP, RELVERIFY, BOOLGADGET,
                (APTR) &sel_brdr, NULL, &fnd_text, 0, (APTR) NULL,
                FND_GAD, NULL },

   sav_gdgt = { NULL,  76,87,48,7,  GADGHCOMP, RELVERIFY, BOOLGADGET,
                (APTR) &sel_brdr, NULL, &sav_text, 0, (APTR) NULL,
                SAV_GAD, NULL },

   add_gdgt = { NULL, 137,87,48,7,  GADGHCOMP, RELVERIFY, BOOLGADGET,
                (APTR) &sel_brdr, NULL, &add_text, 0, (APTR) NULL,
                ADD_GAD, NULL },

   del_gdgt = { NULL, 198,87,48,7,  GADGHCOMP, RELVERIFY, BOOLGADGET,
                (APTR) &sel_brdr, NULL, &del_text, 0, (APTR) NULL,
                DEL_GAD, NULL },

   prv_gdgt = { NULL, 259,87,24,7,  GADGIMAGE | GADGHCOMP,
                RELVERIFY, BOOLGADGET,
                (APTR) &prv_img,  NULL, NULL,      0, (APTR) NULL,
                PRV_GAD, NULL },

   nxt_gdgt = { NULL, 283,87,24,7,  GADGIMAGE | GADGHCOMP,
                RELVERIFY, BOOLGADGET,
                (APTR) &nxt_img,  NULL, NULL,      0, (APTR) NULL,
                NXT_GAD, NULL },

   neg_gdgt = { NULL,      222,3,24,8, GADGHNONE,
                RAWKEY | TOGGLESELECT | ENDGADGET, BOOLGADGET | REQGADGET,
                (APTR) &neg_brdr, NULL, &neg_text, 0, (APTR) NULL,
                NEG_GAD, NULL },

   pos_gdgt = { &neg_gdgt, 182,3,24,8, GADGHNONE,
                RAWKEY | TOGGLESELECT | ENDGADGET, BOOLGADGET | REQGADGET,
                (APTR) &neg_brdr, NULL, &pos_text, 0, (APTR) NULL,
                POS_GAD, NULL };

struct Requester
   vfy_rqst = { NULL, 11,83,299,14, 0,0, &pos_gdgt, NULL, NULL, 0, YLWP,
                NULL, NULL, NULL, NULL, NULL };

struct NewWindow
   NewWdw = { 160,11,320,100,  BLKP,WHTP,
              CLOSEWINDOW   | GADGETUP   | RAWKEY      | MOUSEBUTTONS,
              WINDOWCLOSE   | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH |
              NOCAREREFRESH | RMBTRAP    | ACTIVATE,
              NULL, NULL, NULL, NULL, NULL, 0,0,0,0, WBENCHSCREEN };

/*----------------------------- Main program -----------------------------*/

_main()
{
   init();

   StartKey(&BookIndexFile);
   next();

   moveptr(&nxt_gdgt,0);

   while (TRUE) {

      Wait(MESSAGE);

      while (msg = (struct IntuiMessage *) GetMsg(wdw->UserPort))  {
         class = msg->Class;
         code  = msg->Code;
         qual  = msg->Qualifier;
         iadr  = msg->IAddress;

         ReplyMsg(msg);

         if (class == GADGETUP)
            gid = ((struct Gadget *) iadr)->GadgetID;
         else
            gid = 0;

         if (!IGNORE_MSG) {

            if ((find_flag) && (gid != NAM_GAD))
               kill_find();

            if (ABORT_MSG) {
               moveptr(&nxt_gdgt,0);
            } else {
               switch (class) {
                  case GADGETUP:
                     dispatch_gad();
                     break;
                  case RAWKEY:
                     dispatch_key();
                     break;
                  case CLOSEWINDOW:
                     quit(0);
                     break;
               }
            }
         }
      }
   }
}

/*--------------------------- Function dispatch --------------------------*/

dispatch_gad()
{
   switch (gid) {
      case FND_GAD:
         set_find();
         break;
      case ADD_GAD:
         add();
         break;
      case SAV_GAD:
         save();
         break;
      case DEL_GAD:
         delete();
         break;
      case PRV_GAD:
         prev();
         break;
      case NXT_GAD:
         next();
         break;
      case NAM_GAD:
         if (find_flag)
            find();
         else
            moveptr(&adr_gdgt,1);
         break;
      case ADR_GAD:
         moveptr(&cit_gdgt,1);
         break;
      case CIT_GAD:
         moveptr(&sta_gdgt,1);
         break;
      case STA_GAD:
         moveptr(&zip_gdgt,1);
         break;
      case ZIP_GAD:
         moveptr(&phn_gdgt,1);
         break;
      case PHN_GAD:
         moveptr(&not_gdgt,1);
         break;
      case NOT_GAD:
         moveptr(&nxt_gdgt,0);
         break;
   }
}

dispatch_key()
{
   switch (code) {
      case FND_KEY:
         set_find();
         break;
      case SAV_KEY:
         moveptr(&sav_gdgt,0);
         save();
         break;
      case ADD_KEY:
         moveptr(&add_gdgt,0);
         add();
         break;
      case DEL_KEY:
         moveptr(&del_gdgt,0);
         delete();
         break;
      case PRV_KEY:
         moveptr(&prv_gdgt,0);
         prev();
         break;
      case NXT_KEY:
         moveptr(&nxt_gdgt,0);
         next();
         break;
      case EDT_KEY:
         moveptr(&nam_gdgt,1);
         break;
   }
}

/*--------------------------- Function handlers --------------------------*/

set_find()
{
   if (UsedRecs(&BookDataFile)) {
      chkrec();
      setmem(&BookRec,sizeof(BookRec),'\0');
      showrec();
      find_flag = TRUE;
      moveptr(&nam_gdgt,1);
   }
}

find()
{
   if (strlen(BookRec.Name)) {
      genkey(BookRec.Name,BookKey,sizeof(BKEY));
      SearchKey(&BookIndexFile,&BookRecNum,BookKey);
      if (!BFILE_OK)
         PrevKey(&BookIndexFile,&BookRecNum,BookKey);
      readrec();
   } else {
      kill_find();
   }
   moveptr(&fnd_gdgt,0);
}

kill_find()
{
   BookRec = SaveRec;
   showrec();
   find_flag = FALSE;
}

save()
{
   BKEY  tmpkey;

   if (UsedRecs(&BookDataFile)) {
      PutRec(&BookDataFile,BookRecNum,&BookRec);
      genkey(SaveRec.Name,tmpkey,sizeof(BKEY));
      genkey(BookRec.Name,BookKey,sizeof(BKEY));
      if (strncmp(tmpkey,BookKey,sizeof(BKEY))) {
         DeleteKey(&BookIndexFile,&BookRecNum,tmpkey);
         AddKey(&BookIndexFile,BookRecNum,BookKey);
         FindKey(&BookIndexFile,&BookRecNum,BookKey);
      }
      SaveRec = BookRec;
   }
}

add()
{
   if (strlen(BookRec.Name)) {
      genkey(BookRec.Name,BookKey,sizeof(BKEY));
      FindKey(&BookIndexFile,&BookRecNum,BookKey);
      if (BFILE_OK)
         BFILE_OK = !verify(&avf_text);
      if (!BFILE_OK) {
         AddRec(&BookDataFile,&BookRecNum,&BookRec);
         genkey(BookRec.Name,BookKey,sizeof(BKEY));
         AddKey(&BookIndexFile,BookRecNum,BookKey);
         FindKey(&BookIndexFile,&BookRecNum,BookKey);
         showusd();
      }
      SaveRec = BookRec;
   }
}

delete()
{
   if (UsedRecs(&BookDataFile)) {
      if (verify(&dvf_text)) {
         DeleteKey(&BookIndexFile,&BookRecNum,BookKey);
         DeleteRec(&BookDataFile,BookRecNum);
         SearchKey(&BookIndexFile,&BookRecNum,BookKey);
         if (!BFILE_OK)
            PrevKey(&BookIndexFile,&BookRecNum,BookKey);
         showusd();
         readrec();
      }
   }
}

next()
{
   if (UsedRecs(&BookDataFile)) {
      chkrec();
      NextKey(&BookIndexFile,&BookRecNum,BookKey);
      if (!BFILE_OK) {
         DisplayBeep(NULL);
         PrevKey(&BookIndexFile,&BookRecNum,BookKey);
      }
      readrec();
   }
}

prev()
{
   if (UsedRecs(&BookDataFile)) {
      chkrec();
      PrevKey(&BookIndexFile,&BookRecNum,BookKey);
      if (!BFILE_OK) {
         DisplayBeep(NULL);
         NextKey(&BookIndexFile,&BookRecNum,BookKey);
      }
      readrec();
   }
}

/*---------------------------- Initialization ----------------------------*/

init()
{
   struct Gadget  *p;
   int             x1,y1,x2,y2;
   register int    i;

   if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library",0)))
      quit(-1);

   if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0)))
      quit(-2);

   if (!(dev = CreatePort(0,0)))
      quit(-3);

   if (!(req = CreateStdIO(dev)))
      quit(-4);

   if (OpenDevice("input.device",0,req,0))
      quit(-5);

   req->io_Command = IND_WRITEEVENT;
   req->io_Flags   = 0;
   req->io_Length  = sizeof(struct InputEvent);

   OpenFile(&BookDataFile,"BLKBOOK.DAT",sizeof(BookRec));
   if (BFILE_OK)
      OpenIndex(&BookIndexFile,"BLKBOOK.IDX",sizeof(BKEY),DUP);
   if (!BFILE_OK) {
      MakeFile(&BookDataFile,"BLKBOOK.DAT",sizeof(BookRec));
      if (BFILE_OK)
         MakeIndex(&BookIndexFile,"BLKBOOK.IDX",sizeof(BKEY),DUP);
   }

   if (!BFILE_OK)
      quit(-6);

   if (!(wdw = (struct Window *) OpenWindow(&NewWdw)))
      quit(-7);

   rp = wdw->RPort;

   if (font = OpenFont(&attr))
      SetFont(rp,font);

   SetWindowTitles(wdw,wdwttl,scrttl);

   SetAPen(rp,BLKP);
   RectFill(rp,1,10,318,98);

   DrawBorder(rp,&sel_brdr,258,87);

   SetAPen(rp,BLUP);
   for (i = 1, p = &nam_gdgt; i <= WDW_GAD; i++, p++) {
      if (p->GadgetID <= STR_GAD) {
         x1 = p->LeftEdge;
         y1 = p->TopEdge;
         x2 = x1 + p->Width - 1;
         y2 = y1 + p->Height - 1;
         RectFill(rp,x1,y1,x2,y2);
      }
      AddGadget(wdw,p,-1);
   }

   SetAPen(rp,WHTP);

   RefreshGadgets(wdw->FirstGadget,wdw,NULL);

   showusd();
}

/*---------------------------- Misc functions ----------------------------*/

readrec()
{
   GetRec(&BookDataFile,BookRecNum,&BookRec);
   showrec();
   genkey(BookRec.Name,BookKey,sizeof(BKEY));
   SaveRec = BookRec;
}

showrec()
{
   struct StringInfo  *p;
   register int        i;

   for (i = 1, p = &nam_info; i <= STR_GAD; i++, p++)
      pad(p->Buffer,p->MaxChars);

   strncpy(Note,BookRec.Note,sizeof(Note));

   PrintIText(rp,&nam_disp,0,0);

   for (i = 1, p = &nam_info; i <= STR_GAD; i++, p++)
      p->NumChars = trim(p->Buffer);
}

pad(str,len)
   char  *str;
   int    len;
{
   setmem(&str[strlen(str)],len - strlen(str),' ');
   str[len - 1] = '\0';
}

trim(str)
   char  *str;
{
   register int  i;

   for (i = strlen(str) - 1; (i >= 0) && (str[i] == ' '); str[i--] = '\0');
   return(++i);
}

showusd()
{
   sprintf(used,"%5d",UsedRecs(&BookDataFile));
   PrintIText(rp,&usd_text,270,15);
}

genkey(src,dst,len)
   char  *src;
   char  *dst;
   int    len;
{
   while (len--)
      (*src) ? (*dst++ = toupper(*src++)) : (*dst++ = ' ');
}

chkrec()
{
      if (strcmp(BookRec.Name ,SaveRec.Name ) ||
          strcmp(BookRec.Addr ,SaveRec.Addr ) ||
          strcmp(BookRec.City ,SaveRec.City ) ||
          strcmp(BookRec.State,SaveRec.State) ||
          strcmp(BookRec.Zip  ,SaveRec.Zip  ) ||
          strcmp(BookRec.Phone,SaveRec.Phone) ||
          strcmp(BookRec.Note ,SaveRec.Note ))
            if (verify(&svf_text))
               save();
}

moveptr(gad,butn)
   struct Gadget *gad;
   int            butn;
{
   epos.ie_X = ebut.ie_X =
      wdw->LeftEdge + gad->LeftEdge + gad->Width - 2;
   epos.ie_Y = ebut.ie_Y =
      (wdw->TopEdge + gad->TopEdge + gad->Height - 1) << 1;

   if (butn)
      req->io_Data = (APTR) &ebut;
   else
      req->io_Data = (APTR) &epos;

   DoIO(req);
}

verify(txt)
   struct IntuiText  *txt;
{
   vfy_rqst.ReqText = txt;
   pos_gdgt.Flags &= ~SELECTED;
   neg_gdgt.Flags &= ~SELECTED;

   Request(&vfy_rqst,wdw);

   Wait(MESSAGE);
   msg = (struct IntuiMessage *) GetMsg(wdw->UserPort);
   ReplyMsg(msg);

   return((int) pos_gdgt.Flags & SELECTED);
}

/*----------------------------- Termination ------------------------------*/

quit(err)
   int   err;
{
   CloseFile(&BookDataFile);
   CloseIndex(&BookIndexFile);

   if (req) {
      CloseDevice(req);
      DeleteStdIO(req);
   }

   if (dev)
      DeletePort(dev);

   if (font)
      CloseFont(font);

   if (wdw)
      CloseWindow(wdw);

   if (GfxBase)
      CloseLibrary(GfxBase);

   if (IntuitionBase)
      CloseLibrary(IntuitionBase);

   OpenWorkBench();

   exit(err);
}

