#include "rolodex.h"

#ifdef HAVE_XCURSES
char *XCursesProgramName="rolodex";
#endif

/*
 * This is the main part of the loop.
 */
int main(int argc, char **argv)
{
   /* Declare variables. */
   CDKSCREEN *cdkscreen;
   CDKMENU *rolodexMenu;
   CDKLABEL *rolodexTitle;
   SRolodex groupList[MAXGROUPS];
   WINDOW *cursesWin;
   char *menulist[MAX_MENU_ITEMS][MAX_SUB_ITEMS], *title[5];
   char *home, temp[256], *mesg[15];
   int subMenuSize[10], menuLocations[10], selection;
   int group, ret, x;
   int groupCount = 0;

   /* Set up CDK  */ 
   cursesWin = initscr();
   cdkscreen = initCDKScreen (cursesWin);

   /* Start CDK color. */
   initCDKColor();

   /* Create the menu lists. */
   menulist[0][0] = "</U>File";menulist[1][0] = "</U>Groups";
   menulist[0][1] = "</B/5>Open   ";menulist[1][1] = "</B/5>New   ";
   menulist[0][2] = "</B/5>Save   ";menulist[1][2] = "</B/5>Open  ";
   menulist[0][3] = "</B/5>Save As";menulist[1][3] = "</B/5>Delete";
   menulist[0][4] = "</B/5>Quit   ";

   menulist[2][0] = "</U>Print";menulist[3][0] = "</U>Help";
   menulist[2][1] = "</B/5>Print Groups";menulist[3][1] = "</B/5>About Rolodex     ";
   menulist[3][2] = "</B/5>Rolodex Statistics";

   /* Set up the sub-menu sizes and their locations. */
   subMenuSize[0] = 5;menuLocations[0] = LEFT;
   subMenuSize[1] = 4;menuLocations[1] = LEFT;
   subMenuSize[2] = 2;menuLocations[2] = LEFT;
   subMenuSize[3] = 3;menuLocations[3] = RIGHT;

   /* Create the menu. */
   rolodexMenu = newCDKMenu (cdkscreen, menulist, 4,
				subMenuSize, menuLocations,
				TOP, A_BOLD|A_UNDERLINE, A_REVERSE);
   
   /* Create the title. */
   title[0]= "<C></U>Cdk Rolodex";
   title[1]= "<C></B/24>Written by Mike Glover";
   rolodexTitle = newCDKLabel (cdkscreen, CENTER, CENTER, title, 2, FALSE, FALSE);

   /* Define the help key binding. */
   bindCDKObject (vMENU, rolodexMenu, '?', helpCB, NULL);

   /* Draw the CDK screen. */
   refreshCDKScreen (cdkscreen);
    
   /* Check the value of the HOME env var. */
   home = getenv ("HOME");
   if (home != (char *)NULL)
   {
      /* Make sure the $HOME/.rolodex directory exists. */
      sprintf (temp, "%s/.rolodex", home);

      /* Set the value of the global rolodex DBM directory. */
      GDBMDir = copyChar (temp);

      /* Set the value of the global RC filename. */
      sprintf (temp, "%s/.rolorc", home);
      GRCFile = copyChar (temp);
   }
   else
   {
      /* Set the value of the global rolodex DBM directory. */
      GDBMDir = copyChar (".rolodex");

      /* Set the value of the global RC filename. */
      GRCFile = copyChar (".rolorc");
   }

   /* Make the rolodex directory. */
   mkdir (GDBMDir, 0755);

   /* Open the rolodex RC file. */
   groupCount = readRCFile (GRCFile, groupList);

   /* Check the value of groupCount. */
   if (groupCount < 0)
   {
      /* The RC file seems to be corrupt. */
      sprintf (temp, "<C></B/16>The RC file (%s) seems to be corrupt.", GRCFile);
      mesg[0] = copyChar (temp);
      mesg[1] = "<C></B/16>No rolodex groups were loaded.";
      mesg[2] = "<C>Press any key to continue.";
      popupLabel (cdkscreen, mesg, 3);
      freeChar (mesg[0]);
      groupCount = 0;
   }
   else if (groupCount == 0)
   {
      mesg[0] = "<C></B/24>Empty rolodex RC file. No groups loaded.";
      mesg[1] = "<C>Press any key to continue.";
      popupLabel (cdkscreen, mesg, 2);
   }
   else
   {
      if (groupCount == 1)
      {
         sprintf (temp, "<C></24>There was 1 group loaded from the RC file.");
      }
      else
      {
         sprintf (temp, "<C></24>There were %d groups loaded from the RC file.", groupCount);
      }
      mesg[0] = copyChar (temp);
      mesg[1] = "<C>Press any key to continue.";
      popupLabel (cdkscreen, mesg, 2);
      freeChar (mesg[0]);
   }

   /* Loop until we are done. */
   for (;;)
   {
      /* Activate the menu */
      selection= activateCDKMenu (rolodexMenu, (chtype *)NULL);

      /* Check the return value of the selection. */
      if (selection == 0)
      {
         /* Open the rolodex RC file. */
         groupCount = openNewRCFile (cdkscreen, groupList, groupCount);
      }
      else if (selection == 1)
      {
         /* Write out the RC file. */
         ret = writeRCFile (cdkscreen, GRCFile, groupList, groupCount);

         /* Reset the saved flag if the rc file saved ok. */
         if (ret != 0)
         {
            GGroupModified = 0;
         }
      }
      else if (selection == 2)
      {
         /* Save as. */
         ret = writeRCFileAs (cdkscreen, groupList, groupCount);

         /* Reset the saved flag if the rc file saved ok. */
         if (ret != 0)
         {
            GGroupModified = 0;
         }
      }
      else if (selection == 3)
      {
         /* Has anything changed??? */
         if (GGroupModified != 0)
         {
            /* Write out the RC file. */
            writeRCFile (cdkscreen, GRCFile, groupList, groupCount);
         }

         /* Remove the CDK widget pointers. */
         destroyCDKMenu (rolodexMenu);
         destroyCDKLabel (rolodexTitle);
         destroyCDKScreen (cdkscreen);
         delwin (cursesWin);

         /* Free up other pointers. */
         freeChar (GCurrentGroup);
         freeChar (GRCFile);
         freeChar (GDBMDir);
      
         /* Clean up the group information. */
         for (x=0; x < groupCount; x++)
         {
            freeChar (groupList[x].name);
            freeChar (groupList[x].desc);
            freeChar (groupList[x].dbm);
         }

         /* Shutdown CDK. */
         endCDK();

         /* Exit cleanly. */
         exit (0);
      }
      else if (selection == 100)
      {
         /* Add a new group to the list. */
         groupCount = addRolodexGroup (cdkscreen, groupList, groupCount);
      }
      else if (selection == 101)
      {
         /* If there are no groups, ask them if they want to create a new one. */
         if (groupCount == 0)
         {
               char *buttons[] = {"<Yes>", "<No>"};
               mesg[0] = "<C>There are no groups defined.";
               mesg[1] = "<C>Do you want to define a new group?";

               /* Add the group if they said yes. */
               if (popupDialog (cdkscreen, mesg, 2, buttons, 2) == 0)
               {
                  groupCount = addRolodexGroup (cdkscreen, groupList, groupCount);
               }
         }
         else
         {
            /* Get the number of the group to open. */
            group = pickRolodexGroup (cdkscreen, "<C></B/29>Open Rolodex Group", 
					groupList, groupCount);

            /* Make sure a group was picked. */
            if (group >= 0)
            {
               /* Set the global variable GCurrentGroup */
               freeChar (GCurrentGroup);
               GCurrentGroup = copyChar (groupList[group].name);

               /* Try to open the DBM file and read the contents. */
               useRolodexGroup (cdkscreen,
				groupList[group].name,
				groupList[group].desc,
				groupList[group].dbm);
            }
         }
      }
      else if (selection == 102)
      {
         /* Delete the group chosen. */
         groupCount = deleteRolodexGroup (cdkscreen, groupList, groupCount);
      }
      else if (selection == 200)
      {
         /* Print Phone Number Group. */
         printGroupNumbers (cdkscreen, groupList, groupCount);
      }
      else if (selection == 300)
      {
         /* About Rolodex. */
         aboutCdkRolodex(cdkscreen);
      }
      else if (selection == 301)
      {
         displayRolodexStats (rolodexMenu->screen, groupCount);
      }
   }
}

/*
 * This writes out the new RC file.
 */
int writeRCFile (CDKSCREEN *screen, char *filename, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   char *mesg[5], temp[256];
   time_t clck;
   FILE *fd;
   int x;

   /* Can we open the file?                                     */
   if ((fd = fopen (filename, "w")) == NULL)
   {
      /* Pop up a message. */
      sprintf (temp, "</B/16>The file <%s> could not be opened.", filename);
      mesg[0] = copyChar (temp);
#ifdef NOSTRERR
      sprintf (temp, "<C></B/16>Unknown reason.");
#else
      sprintf (temp, "<C></B/16>%s", strerror(errno));
#endif
      mesg[1] = copyChar (temp);
      mesg[2] = "<C>Press any key to continue.";

      popupLabel (screen, mesg, 3);

      freeChar (mesg[0]);
      freeChar (mesg[1]);

      return (0);
   }

   /* Get the current time. */
   time(&clck);

   /* Put some comments at the top of the header. */
   fprintf (fd, "#\n");
   fprintf (fd, "# This file was automatically generated on %s", ctime(&clck));
   fprintf (fd, "#\n");

   /* Start writing the RC file. */
   for (x=0; x < groupCount; x++)
   {
      fprintf (fd, "%s%s%s\n", groupList[x].name, groupList[x].desc, groupList[x].dbm);
   }
   fclose (fd);

   /* Pop up a message stating that it has been saved. */
   if (groupCount == 1)
   {
      sprintf (temp, "There was 1 group saved to file");
      mesg[0] = copyChar (temp);
   }
   else
   {
      sprintf (temp, "There were %d groups saved to file", groupCount);
      mesg[0] = copyChar (temp);
   }

   sprintf (temp, "<C>%s", filename);
   mesg[1] = copyChar (temp);
   mesg[2] = "<C>Press any key to continue.";

   popupLabel (screen, mesg, 3);
   freeChar (mesg[0]); freeChar (mesg[1]);
   return (1);
}

/*
 * This allows the user to pick a DBM file to open.
 */
int pickRolodexGroup (CDKSCREEN *screen, char *title, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   CDKSCROLL *roloList	= (CDKSCROLL *)NULL;
   int height		= groupCount;
   char *mesg[MAXGROUPS], temp[256];
   int selection, x;

   /* Determine the height of the scrolling list. */
   if (groupCount > 5)
   {
      height = 5;
   }
   height += 3;

   /* Copy the names of the scrolling list into an array. */
   for (x=0; x < groupCount; x++)
   {
      sprintf (temp, "<C></B/29>%s", groupList[x].name);
      mesg[x] = copyChar (temp);
   }

   /* Create the scrolling list. */
   roloList = newCDKScroll (screen, CENTER, CENTER, NONE,
				height, 50, title, mesg, groupCount,
				NONUMBERS, A_REVERSE, TRUE, FALSE);

   /* Create a callback to the scrolling list. */
   bindCDKObject (vSCROLL, roloList, '?', groupInfoCB, groupList);

   /* Activate the scrolling list. */
   selection = activateCDKScroll (roloList, (chtype *)NULL);

   /* Destroy the scrolling list. */
   destroyCDKScroll (roloList);
   for (x=0; x < groupCount; x++)
   {
      freeChar (mesg[x]);
   }

   /* Return the item selected. */
   return selection;
}

/*
 * This allows the user to add a rolo group to the list.
 */
int addRolodexGroup (CDKSCREEN *screen, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   CDKENTRY *newName= (CDKENTRY *)NULL;
   CDKENTRY *newDesc= (CDKENTRY *)NULL;
   char *mesg[4], *desc, *newGroupName, temp[256];
   int x;

   /* Create the name widget. */
   newName = newCDKEntry (screen, CENTER, 8,
				"<C></B/29>New Group Name",
				"</B/29>   Name: ",
				A_NORMAL, '_', vMIXED,
				20, 2, 256, TRUE, FALSE);


   /* Get the name. */
   newGroupName = activateCDKEntry (newName, (chtype *)NULL);

   /* Make sure they didn't hit escape. */
   if (newName->exitType == vESCAPE_HIT)
   {
      mesg[0] = "<C></B/16>Add Group Canceled.";
      destroyCDKEntry (newName);
      popupLabel (screen, mesg, 1);
      return groupCount;
   }

   /* Make sure that group name does not already exist. */
   for (x=0; x < groupCount; x++)
   {
      if (strcmp (newGroupName, groupList[x].name) == 0)
      {
         sprintf (temp, "<C></B/16>Sorry the group (%s) already exists.", newGroupName);
         mesg[0] =  copyChar (temp);
         popupLabel (screen, mesg, 1);
         freeChar (mesg[0]);
         destroyCDKEntry (newName);
         return groupCount;
      }
   }

   /* Keep the name. */
   groupList[groupCount].name = copyChar (newGroupName);

   /* Create the description widget. */
   newDesc = newCDKEntry (screen, CENTER, 13,
				"<C></B/29>Group Description",
				"</B/29>Descriprion: ",
				A_NORMAL, '_', vMIXED,
				20, 2, 256, TRUE, FALSE);

   /* Get the description. */
   desc = activateCDKEntry (newDesc, (chtype *)NULL);

   /* Check if they hit escape or not. */
   if (newDesc->exitType == vESCAPE_HIT)
   {
      groupList[groupCount].desc = copyChar ("No Description Provided.");
   }
   else
   {
      groupList[groupCount].desc = copyChar (desc);
   }

   /* Create the DBM filename. */
   sprintf (temp, "%s/%s.phl", GDBMDir, groupList[groupCount].name);
   groupList[groupCount].dbm = copyChar (temp);

   /* Inrement the group count. */
   groupCount++;
   GGroupModified = 1;

   /* Destroy the widgets. */
   destroyCDKEntry (newName);
   destroyCDKEntry (newDesc);
   return groupCount;
}

/*
 * This displays rolodex information.
 */
void displayRolodexStats (CDKSCREEN *screen, int groupCount)
{
   /* Declare local variables. */
   char *mesg[6], temp[256];

   /* Create the information to display. */
   sprintf (temp, "<C></U>Rolodex Statistics");
   mesg[0] = copyChar (temp);
   sprintf (temp, "</B/5>Read Command Filename<!B!5> </U>%s<!U>", GRCFile);
   mesg[1] = copyChar (temp);
   sprintf (temp, "</B/5>Group Count          <!B!5> </U>%d<!U>", groupCount);
   mesg[2] = copyChar (temp);

   /* Display the message. */
   popupLabel (screen, mesg, 3);
 
   /* Clean up. */
   freeChar (mesg[0]); freeChar (mesg[1]); freeChar (mesg[2]);
}

/*
 * This function gets a new rc filename and saves the contents of the
 * groups under that name.
 */
int writeRCFileAs (CDKSCREEN *screen, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   CDKENTRY *newRCFile;
   char *newFilename;
   int ret;

   /* Create the entry field. */
   newRCFile = newCDKEntry (screen, CENTER, CENTER,
				"<C></R>Save As",
				"Filename: ",
				A_NORMAL, '_', vMIXED, 
				20, 2, 256, TRUE, FALSE);

   /* Add a pre-process function so no spaces are introduced. */
   setCDKEntryPreProcess (newRCFile, entryPreProcessCB, NULL);

   /* Get the filename. */
   newFilename = activateCDKEntry (newRCFile, NULL);

   /* Check if they hit escape or not. */
   if (newRCFile->exitType == vESCAPE_HIT)
   {
      destroyCDKEntry (newRCFile);
      return 1;
   }

   /* Call the function to save the RC file. */
   ret = writeRCFile (screen, newFilename, groupList, groupCount);
 
   /* Reset the saved flag if the rc file saved ok. */
   if (ret != 0)
   {
      /* Change the default filename. */
      freeChar (GRCFile);
      GRCFile = copyChar (newFilename);
      GGroupModified = 0;
   }

   /* Clean up. */
   destroyCDKEntry (newRCFile);
   return 1;
}

/* 
 * This opens a new RC file.
 */
int openNewRCFile (CDKSCREEN *screen, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   CDKFSELECT *fileSelector;
   char *filename, *mesg[10], temp[256];
   int x;

   /* Get the filename. */
   fileSelector = newCDKFselect (screen, CENTER, CENTER, 20, 55,
				"<C>Open RC File",
				"Filename: ",
				A_NORMAL, '.', A_REVERSE,
				"</5>", "</48>", "</N>", "</N>",
				TRUE, FALSE);
 
   /* Activate the file selector. */
   filename = activateCDKFselect (fileSelector, NULL);

   /* Check if the file selector left early. */
   if (fileSelector->exitType == vESCAPE_HIT)
   {
      destroyCDKFselect (fileSelector);
      mesg[0] = "Open New RC File Aborted.";
      popupLabel (screen, mesg, 1);
      return groupCount;
   }

   /* Clean out the old information. */
   for (x=0; x < groupCount; x++)
   {
      freeChar (groupList[x].name);
      freeChar (groupList[x].desc);
      freeChar (groupList[x].dbm);
   }

   /* Open the RC file. */
   groupCount = readRCFile (filename, groupList);

   /* Check the return value. */
   if (groupCount < 0)
   {
      /* This file does not appear to be a rolodex file. */
      mesg[0] = "<C></B/16>The file<!B!16>";
      sprintf (temp, "<C></B/16>(%s)<!B!16>", filename);
      mesg[1] = copyChar (temp);
      mesg[2] = "<C>does not seem to be a rolodex RC file.";
      mesg[3] = "<C>Press any key to continue.";
      popupLabel (screen, mesg, 4);
      freeChar (mesg[1]);
      groupCount = 0;
   }

   /* Clean up. */
   destroyCDKFselect (fileSelector);
   return groupCount;
}

/*
 * This reads the users rc file.
 */
int readRCFile (char *filename, SRolodex *groupList)
{
   /* Declare variables. */
   int groupsFound= 0;
   int errorsFound= 0;
   char *lines[MAX_LINES], *items[100];
   int linesRead, chunks, x, y;

   /* Open the file and start reading. */
   linesRead = readFile (filename, lines, MAX_LINES);

   /* Check the number of lines read. */
   if (linesRead == 0)
   {
      return (0);
   }

  /*
   * Cycle through what was given to us and save it.
   */
   for (x=0; x < linesRead; x++)
   {
      /* Strip white space from the line. */
      stripWhiteSpace (vBOTH, lines[x]);

      /* Only split lines which do not start with a # */
      if (strlen (lines[x]) != 0 && lines[x][0] != '#')
      {
         /* Split the line on the = sign. */
         chunks = splitString (lines[x], items, '');

         /* Only take the ones which fit the format. */
         if (chunks == 3)
         {
            /* Clean off the name and DB name. */
            stripWhiteSpace (vBOTH, items[0]);
            stripWhiteSpace (vBOTH, items[1]);
            stripWhiteSpace (vBOTH, items[2]);

            /* Set the group name and DB name. */
            groupList[groupsFound].name = items[0];
            groupList[groupsFound].desc = items[1];
            groupList[groupsFound].dbm = items[2];
            groupsFound++;
         }
         else
         {
            /* Don't forget the clean up the memory. */
            for (y=0; y < chunks; y++)
            {
               freeChar (items[y]);
            }
            errorsFound++;
         }
      }
   }

   /* Clean up. */
   for (x=0; x < linesRead; x++)
   {
      freeChar (lines[x]);
   }

   /* Check the number of groups to the number of errors. */
   if (errorsFound > 0 && groupsFound == 0)
   {
      /* This does NOT look like the rolodex RC file. */
      return -1;
   }
   return groupsFound;
}

/*
 * This function allows the user to add/delete/modify/save the
 * contents of a rolodex group.
 */
void useRolodexGroup (CDKSCREEN *screen, char *groupName, char *groupDesc, char *groupDBM)
{
   /* Declare local variables. */
   CDKSCROLL *nameList= (CDKSCROLL *)NULL;
   CDKLABEL *helpWindow = (CDKLABEL *)NULL;
   SPhoneData phoneData;
   SPhoneRecord *phoneRecord;
   char *index[MAX_ITEMS], *title[3], *mesg[3], temp[256];
   int phoneCount, selection, height, x;

   /* Set up the help window at the bottom of the screen. */
   title[0]     = "<C><#HL(30)>";
   title[1]     = "<C>Press </B>?<!B> to get detailed help.";
   title[2]     = "<C><#HL(30)>";
   helpWindow= newCDKLabel (screen, CENTER, BOTTOM, title, 3, FALSE, FALSE);
   drawCDKLabel (helpWindow, FALSE);

   /* Open the DBM file and read in the contents of the file */
   phoneCount = readPhoneDataFile (groupDBM, &phoneData);
   phoneData.recordCount = phoneCount;

   /* Check the number of entries returned. */
   if (phoneCount == 0)
   {
      /*
       * They tried to open an empty group, maybe they want to
       * add a new entry to this number.
       */ 
      char *buttons[] = {"<Yes>", "<No>"};
      mesg[0] = "<C>There were no entries in this group.";
      mesg[1] = "<C>Do you want to add a new listng?";
      if (popupDialog (screen, mesg, 2, buttons, 2) == 1)
      {
         destroyCDKLabel (helpWindow);
         return;
      }

      /* Get the information for a new number. */
      if (addPhoneRecord (screen, &phoneData) != 0)
      {
         return;
      }
   }
   else if (phoneCount < 0)
   {
      mesg[0] = "<C>Could not open the database for this group.";
      popupLabel (screen, mesg, 1);
      destroyCDKLabel (helpWindow);
      return;
   }

   /* Set up the data needed for the scrolling list. */
   for (x=0; x < phoneData.recordCount; x++)
   {
      phoneRecord = &phoneData.record[x];
      sprintf (temp, "</B/29>%s (%s)", phoneRecord->name, GLineType[phoneRecord->lineType]);
      index[x] = copyChar (temp);
   }
   sprintf (temp, "<C>Listing of Group </U>%s", groupName);
   height = (phoneData.recordCount > 5 ? 8 : phoneData.recordCount+3);

   /* Create the scrolling list. */
   nameList = newCDKScroll (screen, CENTER, CENTER, RIGHT,
				height, 50, temp, index,
				phoneData.recordCount,
				NUMBERS, A_REVERSE, TRUE, FALSE);

   /* Clean up. */
   for (x=0; x < phoneData.recordCount; x++)
   {
      freeChar (index[x]);
   }

   /* Create key bindings. */
   bindCDKObject (vSCROLL, nameList, 'i', insertPhoneEntryCB, &phoneData);
   bindCDKObject (vSCROLL, nameList, 'd', deletePhoneEntryCB, &phoneData);
   bindCDKObject (vSCROLL, nameList, DELETE, deletePhoneEntryCB, &phoneData);
   bindCDKObject (vSCROLL, nameList, '?', phoneEntryHelpCB, NULL);

   /* Let them play. */
   selection = 0;
   while (selection >= 0)
   {
      /* Get the information they want to view. */
      selection = activateCDKScroll (nameList, (chtype *)NULL);

      /* Display the information. */
      if (selection >= 0)
      {
         /* Display the information. */
         displayPhoneInfo (screen, phoneData.record[selection]);
      }
   }

   /* Save the rolodex information to file. */
   if (savePhoneDataFile (groupDBM, &phoneData) == 0)
   {
      /* Something happened. */
      mesg[0] = "<C>Could not save phone data to data file.";
      mesg[1] = "<C>All changes have been lost.";
      popupLabel (screen, mesg, 2);
   }

   /* Clean up. */
   for (x=0; x < phoneData.recordCount; x++)
   {
      freeChar (phoneData.record[x].name);
      freeChar (phoneData.record[x].phoneNumber);
      freeChar (phoneData.record[x].address);
      freeChar (phoneData.record[x].city);
      freeChar (phoneData.record[x].province);
      freeChar (phoneData.record[x].postalCode);
      freeChar (phoneData.record[x].desc);
   }
   destroyCDKScroll (nameList);
   destroyCDKLabel (helpWindow);
}

/*
 * This opens a phone data file and returns the number of elements read.
 */
int readPhoneDataFile (char *dataFile, SPhoneData *phoneData)
{
   /* Declare variables. */
   char *lines[MAX_LINES], *items[30];
   int linesRead= 0;
   int chunks= 0;
   int linesFound= 0;
   int x, y;
 
   /* Open the file and start reading. */
   linesRead = readFile (dataFile, lines, MAX_LINES);
 
   /* Check the number of lines read. */
   if ((linesRead == 0) || (linesRead == -1))
   {
      return (0);
   }
 
  /*
   * Cycle through what was given to us and save it.
   */
   for (x=0; x < linesRead; x++)
   {
      if (lines[x][0] != '#')
      {
         /* Split the string. */
         chunks = 0;
         chunks = splitString (lines[x], items, '');

         /* Copy the chunks. */
         if (chunks == 8)
         {
            phoneData->record[linesFound].name		= items[0];
            phoneData->record[linesFound].lineType	= atoi (items[1]);
            phoneData->record[linesFound].phoneNumber	= items[2];
            phoneData->record[linesFound].address	= items[3];
            phoneData->record[linesFound].city		= items[4];
            phoneData->record[linesFound].province	= items[5];
            phoneData->record[linesFound].postalCode	= items[6];
            phoneData->record[linesFound].desc		= items[7];
            freeChar (items[1]);
            linesFound++;
         }
         else
         {
            /* Bad line in the file; recover the memory. */
            for (y=0; y <= chunks; y++)
            {
               freeChar (items[y]);
            }
         }
      }
   }

   /* Clean up. */
   for (x=0; x < linesRead; x++)
   {
      freeChar (lines[x]);
   }

   /* Keep the record count and return. */
   phoneData->recordCount = linesFound;
   return linesFound;
}

/*
 * This opens a phone data file and returns the number of elements read.
 */
int savePhoneDataFile (char *filename, SPhoneData *phoneData)
{
   /* Declare variables. */
   SPhoneRecord *phoneRecord;
   time_t clck;
   FILE *fd;
   int x;
 
   /* Can we open the file? */
   if ((fd = fopen (filename, "w")) == NULL)
   {
      return 0;
   }

   /* Get the current time. */
   time(&clck);

   /* Add the header to the file. */
   fprintf (fd, "#\n");
   fprintf (fd, "# This file was automatically saved on %s", ctime(&clck));
   fprintf (fd, "# There should be %d phone numbers in this file.\n", phoneData->recordCount);
   fprintf (fd, "#\n");

   /* Cycle through the data and start writing it to the file. */
   for (x=0; x < phoneData->recordCount; x++)
   {
      /* Get a pointer to the object. */
      phoneRecord = &phoneData->record[x];

      /* Check the phone type. */
      if (phoneRecord->lineType == vCell || phoneRecord->lineType == vPager)
      {
         fprintf (fd, "%s%d%s----%s\n",
			phoneRecord->name,
			phoneRecord->lineType,
			phoneRecord->phoneNumber,
			phoneRecord->desc);
      }
      else
      {
         fprintf (fd, "%s%d%s%s%s%s%s%s\n",
			phoneRecord->name,
			phoneRecord->lineType,
			phoneRecord->phoneNumber,
			phoneRecord->address,
			phoneRecord->city,
			phoneRecord->province,
			phoneRecord->postalCode,
			phoneRecord->desc);
      }
   }
   fclose (fd);
   return 1;
}

/*
 * This displays the information about the phone record.
 */
void displayPhoneInfo (CDKSCREEN *screen, SPhoneRecord record)
{
   /* Declare local variables. */
   char *mesg[10], temp[256];

   /* Check the type of line it is. */
   if (record.lineType == vVoice || \
	record.lineType == vData1 || \
	record.lineType == vData2 || \
	record.lineType == vData3 || \
	record.lineType == vFAX1 || \
	record.lineType == vFAX2 || \
	record.lineType == vFAX3)
   {
      /* Create the information to display. */
      sprintf (temp, "<C></U>%s Phone Record", GLineType[record.lineType]);
      mesg[0] = copyChar (temp);

      sprintf (temp, "</B/29>Name        <!B!29>%s", record.name);
      mesg[1] = copyChar (temp);

      sprintf (temp, "</B/29>Phone Number<!B!29>%s", record.phoneNumber);
      mesg[2] = copyChar (temp);

      sprintf (temp, "</B/29>Address     <!B!29>%s", record.address);
      mesg[3] = copyChar (temp);

      sprintf (temp, "</B/29>City        <!B!29>%s", record.city);
      mesg[4] = copyChar (temp);

      sprintf (temp, "</B/29>Province    <!B!29>%s", record.province);
      mesg[5] = copyChar (temp);

      sprintf (temp, "</B/29>Postal Code <!B!29>%s", record.postalCode);
      mesg[6] = copyChar (temp);

      sprintf (temp, "</B/29>Comment     <!B!29>%s", record.desc);
      mesg[7] = copyChar (temp);

      /* Pop the information up on the screen. */
      popupLabel (screen, mesg, 8);

      /* Clean up. */
      freeChar (mesg[0]); freeChar (mesg[1]); freeChar (mesg[2]);
      freeChar (mesg[3]); freeChar (mesg[4]); freeChar (mesg[5]);
      freeChar (mesg[6]); freeChar (mesg[7]);
   }
   else if (record.lineType == vPager || record.lineType == vCell)
   {
      /* Create the information to display. */
      sprintf (temp, "<C></U>%s Phone Record", GLineType[record.lineType]);
      mesg[0] = copyChar (temp);

      sprintf (temp, "</B/29>Name        <!B!29>%s", record.name);
      mesg[1] = copyChar (temp);

      sprintf (temp, "</B/29>Phone Number<!B!29>%s", record.phoneNumber);
      mesg[2] = copyChar (temp);

      sprintf (temp, "</B/29>Comment     <!B!29>%s", record.desc);
      mesg[3] = copyChar (temp);

      /* Pop the information up on the screen. */
      popupLabel (screen, mesg, 4);

      /* Clean up. */
      freeChar (mesg[0]); freeChar (mesg[1]);
      freeChar (mesg[2]); freeChar (mesg[3]);
   }
   else
   {
      mesg[0] = "<C></R>Error<!R> </U>Unknown Phone Line Type";
      mesg[1] = "<C>Can not display information.";
      popupLabel (screen, mesg, 2);
   }
}

/*
 * This function displays a little pop up window discussing this demo.
 */
void aboutCdkRolodex (CDKSCREEN *screen)
{
   char *mesg[15];

   mesg[0] = "<C></U>About Cdk Rolodex";
   mesg[1] = " ";
   mesg[2] = "</B/24>This demo was written to demonstrate the widgets";
   mesg[3] = "</B/24>available with the Cdk library. Not all of the";
   mesg[4] = "</B/24>Cdk widgets are used, but most of them have been.";
   mesg[5] = "</B/24>I hope this little demonstration helps give you an";
   mesg[6] = "</B/24>understanding of what the Cdk library offers.";
   mesg[7] = " ";
   mesg[8] = "<C></B/24>Have fun with it.";
   mesg[9] = " ";
   mesg[10] = "</B/24>ttfn,";
   mesg[11] = "<C></B/24>Mike";
   mesg[12] = "<C><#HL(35)>";
   mesg[13] = "<R></B/24>March 1996";

   popupLabel (screen, mesg, 14);
}

/*
 * This deletes a rolodex group.
 */
int deleteRolodexGroup (CDKSCREEN *screen, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   char *mesg[10], *buttons[5], temp[256];
   int selection, choice, x;

   /* If there are no groups, pop up a message telling them. */
   if (groupCount == 0)
   {
      mesg[0] = "<C>Error";
      mesg[1] = "<C>There as no groups defined.";
      popupLabel (screen, mesg, 2);

      /* Return the current group count. */
      return groupCount;
   }

   /* Get the number of the group to delete. */
   selection = pickRolodexGroup (screen, "<C></U>Delete Which Rolodex Group?", groupList, groupCount);

   /* Check the results. */
   if (selection < 0)
   {
      mesg[0] = "<C>   Delete Canceled   ";
      mesg[1] = "<C>No Group Deleted";
      popupLabel (screen, mesg, 2);
      return groupCount;
   }

   /* Let's make sure they want to delete the group. */
   mesg[0] = "<C></U>Confirm Delete";
   mesg[1] = "<C>Are you sure you want to delete the group";
   sprintf (temp, "<C></R>%s<!R>?", groupList[selection].name);
   mesg[2] = copyChar (temp);
   buttons[0] = "<No>";
   buttons[1] = "<Yes>";
   choice = popupDialog (screen, mesg, 3, buttons, 2);
   freeChar (mesg[2]);

   /* Check the results of the confirmation. */
   if (choice == 0)
   {
      mesg[0] = "<C>   Delete Canceled   ";
      mesg[1] = "<C>No Group Deleted";
      popupLabel (screen, mesg, 2);
      return groupCount;
   }

   /* We need to delete the group file first. */
   unlink (groupList[selection].dbm);

   /* OK, lets delete the group. */
   freeChar (groupList[selection].name);
   freeChar (groupList[selection].desc);
   freeChar (groupList[selection].dbm);
   for (x=selection; x < groupCount-1; x++)
   {
      groupList[x].name = groupList[x+1].name;
      groupList[x].desc = groupList[x+1].desc;
      groupList[x].dbm = groupList[x+1].dbm;
   }
   groupCount--;
   GGroupModified = 1;

   /* Clean up. */
   return groupCount;
}

/*
 * This function gets information about a new phone number.
 */
int addPhoneRecord (CDKSCREEN *screen, SPhoneData *phoneData)
{
   /* Declare local variables. */
   CDKLABEL *title;
   CDKITEMLIST *itemList;
   SPhoneRecord *phoneRecord;
   char *titleMesg[3], *types[GLINETYPECOUNT], temp[256];
   int ret, x;

   /* Get the phone record pointer. */
   phoneRecord = &phoneData->record[phoneData->recordCount];

   /* Create a title label to display. */
   titleMesg[0] = "<C></B/16>Add New Phone Record";
   title = newCDKLabel (screen, CENTER, TOP, titleMesg, 1, FALSE, FALSE);
   drawCDKLabel (title, FALSE);

   /* Create the phone line type list. */
   for (x=0; x < GLINETYPECOUNT; x++)
   {
      sprintf (temp, "<C></U>%s", GLineType[x]);
      types[x] = copyChar (temp);
   }

   /* Get the phone line type. */
   itemList = newCDKItemlist (screen, CENTER, CENTER,
				"<C>What Type Of Line Is It?",
				"Type: ",
				types, GLINETYPECOUNT, 0,
				TRUE, FALSE);
   phoneRecord->lineType = activateCDKItemlist (itemList, NULL);
   destroyCDKItemlist (itemList);

   /* Clean up. */
   for (x=0; x < GLINETYPECOUNT; x++)
   {
      freeChar (types[x]);
   }

   /* Check the return code of the line type question. */
   if (phoneRecord->lineType == -1)
   {
      phoneRecord->lineType = 0;
      return 1;
   }
   else if (phoneRecord->lineType == vPager || phoneRecord->lineType == vCell)
   {
      ret = getSmallPhoneRecord (screen, phoneRecord);
   }
   else
   {
      ret = getLargePhoneRecord (screen, phoneRecord);
   }

   /* Check the return value from the getXXXPhoneRecord function.*/
   if (ret == 0)
   {
      phoneData->recordCount++;
   }

   /* Clean up. */
   destroyCDKLabel (title);

   /* Return the new phone list count. */
   return ret;
}

/*
 * This gets a phone record with all of the details.
 */
int getLargePhoneRecord (CDKSCREEN *screen, SPhoneRecord *phoneRecord)
{
   /* Declare local variables. */
   CDKENTRY *nameEntry, *addressEntry, *cityEntry;
   CDKENTRY *provEntry, *postalEntry, *descEntry;
   CDKTEMPLATE *phoneTemplate;
   char *buttons[5], *mesg[15];
   int ret;

   /* Define the widgets. */
   nameEntry = newCDKEntry (screen, LEFT, 5,
				NULL, "</B/5>Name: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256,
				TRUE, FALSE);
   addressEntry = newCDKEntry (screen, RIGHT, 5,
				NULL, "</B/5>Address: ",
				A_NORMAL,
				'_', vMIXED, 40, 2, 256,
				TRUE, FALSE);
   cityEntry = newCDKEntry (screen, LEFT, 8,
				NULL, "</B/5>City: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256,
				TRUE, FALSE);
   provEntry = newCDKEntry (screen, 29, 8,
				NULL, "</B/5>Province: ",
				A_NORMAL,
				'_', vMIXED, 15, 2, 256,
				TRUE, FALSE);
   postalEntry = newCDKEntry (screen, RIGHT, 8,
				NULL, "</B/5>Postal Code: ",
				A_NORMAL,
				'_', vUMIXED, 8, 2, 256,
				TRUE, FALSE);
   phoneTemplate = newCDKTemplate (screen, LEFT, 11,
				NULL,
				"</B/5>Number: ", 
				"(###) ###-####", 
				"(___) ___-____",
				TRUE, FALSE);
   descEntry = newCDKEntry (screen, RIGHT, 11,
				NULL,
				"</B/5>Description: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256,
				TRUE, FALSE);

   /* Get the phone information. */
   for (;;)
   {
      /* Draw the widgets on the screen. */
      drawCDKEntry (nameEntry, nameEntry->box);
      drawCDKEntry (addressEntry, addressEntry->box);
      drawCDKEntry (cityEntry, cityEntry->box);
      drawCDKEntry (provEntry, provEntry->box);
      drawCDKEntry (postalEntry, postalEntry->box);
      drawCDKTemplate (phoneTemplate, phoneTemplate->box);
      drawCDKEntry (descEntry, descEntry->box);

      /* Activate the entries to get the information. */
      phoneRecord->name		= copyChar (activateCDKEntry (nameEntry, NULL));
      phoneRecord->address	= copyChar (activateCDKEntry (addressEntry, NULL));
      phoneRecord->city		= copyChar (activateCDKEntry (cityEntry, NULL));
      phoneRecord->province	= copyChar (activateCDKEntry (provEntry, NULL));
      phoneRecord->postalCode	= copyChar (activateCDKEntry (postalEntry, NULL));
      activateCDKTemplate (phoneTemplate, NULL);
      phoneRecord->phoneNumber	= mixCDKTemplate (phoneTemplate);
      phoneRecord->desc = copyChar (activateCDKEntry (descEntry, NULL));

      /* Determine if the user wants to submit the info. */
      mesg[0] = "<C></U>Confirm New Phone Entry";
      mesg[1] = "<C>Do you want to add this phone number?";
      buttons[0] = "</B/24><Add Phone Number>";
      buttons[1] = "</B/16><Cancel>";
      buttons[2] = "</B/32><Modify Information>";
      ret = popupDialog (screen, mesg, 2, buttons, 3);

      /* Check the response of the popup dialog box. */
      if (ret == 0)
      {
         /* The user wants to submit the information. */
         destroyCDKEntry (nameEntry);
         destroyCDKEntry (addressEntry);
         destroyCDKEntry (cityEntry);
         destroyCDKEntry (provEntry);
         destroyCDKEntry (postalEntry);
         destroyCDKEntry (descEntry);
         destroyCDKTemplate (phoneTemplate);
         return ret;
      }
      else if (ret == 1)
      {
         /* The user does not want to submit the information. */
         freeChar (phoneRecord->name);
         freeChar (phoneRecord->address);
         freeChar (phoneRecord->city);
         freeChar (phoneRecord->province);
         freeChar (phoneRecord->postalCode);
         freeChar (phoneRecord->phoneNumber);
         freeChar (phoneRecord->desc);
         destroyCDKEntry (nameEntry);
         destroyCDKEntry (addressEntry);
         destroyCDKEntry (cityEntry);
         destroyCDKEntry (provEntry);
         destroyCDKEntry (postalEntry);
         destroyCDKEntry (descEntry);
         destroyCDKTemplate (phoneTemplate);
         return ret;
      }
      else
      {
         /* The user wants to edit the information again. */
         freeChar (phoneRecord->name);
         freeChar (phoneRecord->address);
         freeChar (phoneRecord->city);
         freeChar (phoneRecord->province);
         freeChar (phoneRecord->postalCode);
         freeChar (phoneRecord->phoneNumber);
         freeChar (phoneRecord->desc);
      }
   }
}

/*
 * This gets a small phone record.
 */
int getSmallPhoneRecord (CDKSCREEN *screen, SPhoneRecord *phoneRecord)
{
   /* Declare local variables. */
   CDKENTRY *nameEntry, *descEntry;
   CDKTEMPLATE *phoneTemplate;
   char *buttons[5], *mesg[15];
   int ret;

   /* Define the widgets. */
   nameEntry = newCDKEntry (screen, CENTER, 8,
				NULL, "</B/5>Name: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256,
				TRUE, FALSE);
   phoneTemplate = newCDKTemplate (screen, CENTER, 11,
				NULL, "</B/5>Number: ", 
				"(###) ###-####", 
				"(___) ___-____",
				TRUE, FALSE);
   descEntry = newCDKEntry (screen, CENTER, 14,
				NULL, "</B/5>Description: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256,
				TRUE, FALSE);

   /* Get the phone information. */
   for (;;)
   {
      /* Draw the widgets on the screen. */
      drawCDKEntry (nameEntry, nameEntry->box);
      drawCDKTemplate (phoneTemplate, phoneTemplate->box);
      drawCDKEntry (descEntry, descEntry->box);

      /* Activate the entries to get the information. */
      phoneRecord->name		= copyChar (activateCDKEntry (nameEntry, NULL));
      activateCDKTemplate (phoneTemplate, NULL);
      phoneRecord->phoneNumber	= mixCDKTemplate (phoneTemplate);
      phoneRecord->desc		= copyChar (activateCDKEntry (descEntry, NULL));
      phoneRecord->address	= copyChar ("-");
      phoneRecord->city		= copyChar ("-");
      phoneRecord->province	= copyChar ("-");
      phoneRecord->postalCode	= copyChar ("-");

      /* Determine if the user wants to submit the info. */
      mesg[0] = "<C></B/5>Confirm New Phone Entry";
      mesg[1] = "<C>Do you want to add this phone number?";
      buttons[0] = "</B/24><Add Phone Number>";
      buttons[1] = "</B/16><Cancel>";
      buttons[2] = "</B/8><Modify Information>";
      ret = popupDialog (screen, mesg, 2, buttons, 3);

      /* Check the response of the popup dialog box. */
      if (ret == 0)
      {
         /* The user wants to submit the information. */
         destroyCDKEntry (nameEntry);
         destroyCDKEntry (descEntry);
         destroyCDKTemplate (phoneTemplate);
         return ret;
      }
      else if (ret == 1)
      {
         /* The user does not want to submit the information. */
         freeChar (phoneRecord->name);
         freeChar (phoneRecord->phoneNumber);
         freeChar (phoneRecord->desc);
         freeChar (phoneRecord->address);
         freeChar (phoneRecord->city);
         freeChar (phoneRecord->province);
         freeChar (phoneRecord->postalCode);

         destroyCDKEntry (nameEntry);
         destroyCDKEntry (descEntry);
         destroyCDKTemplate (phoneTemplate);
         return ret;
      }
      else
      {
         /* The user wants to edit the information again. */
         freeChar (phoneRecord->name);
         freeChar (phoneRecord->phoneNumber);
         freeChar (phoneRecord->desc);
         freeChar (phoneRecord->address);
         freeChar (phoneRecord->city);
         freeChar (phoneRecord->province);
         freeChar (phoneRecord->postalCode);
      }
   }
}

/*
 * This prints a groups phone numbers.
 */
void printGroupNumbers (CDKSCREEN *screen, SRolodex *groupList, int groupCount)
{
   /* Declare local variables. */
   CDKSELECTION *selectionList;
   CDKENTRY *entry;
   CDKLABEL *title;
   char *itemList[MAX_ITEMS], *mesg[10], temp[256];
   char *choices[] = {"Print to Printer ", "Print to File", "Don't Print"};
   char *filename = (char *)NULL;
   char *printer = (char *)NULL;
   char *defaultPrinter = (char *)NULL;
   int height = groupCount;
   int x;

   /* Create the group list. */
   for (x=0; x < groupCount; x++)
   {
      itemList[x] = copyChar (groupList[x].name);
   }

   /* Set the height of the selection list. */
   if (groupCount > 5)
   {
      height = 5;
   }
   height += 3;

   /* Create the selection list. */
   selectionList = newCDKSelection (screen, CENTER, CENTER, RIGHT,
					height, 40,
					"<C></U>Select Which Groups To Print",
					itemList, groupCount,
					choices, 3,
					A_REVERSE, TRUE, FALSE);

   /* Activate the selection list. */
   if (activateCDKSelection (selectionList, NULL) == -1)
   {
      /* Tell the user they exited early. */
      destroyCDKSelection (selectionList);
      mesg[0] = "<C>Print Canceled.";
      popupLabel (screen, mesg, 1);

      /* Clean up. */
      for (x=0; x < groupCount; x++)
      {
         freeChar (itemList[x]);
      }
      return;
   }
   eraseCDKSelection (selectionList);

   /* Determine which groups we want to print. */
   for (x=0; x < groupCount; x++)
   {
      if (selectionList->selections[x] == 0)
      {
         /* Create a title. */
         sprintf (temp, "<C></R>Printing Group [%s] to Printer", groupList[x].name);
         mesg[0] = copyChar (temp);
         title = newCDKLabel (screen, CENTER, TOP, mesg, 1, FALSE, FALSE);
         drawCDKLabel (title, FALSE);
         freeChar (mesg[0]);

         /* Get the printer name to print to. */
         entry = newCDKEntry (screen, CENTER, 8,
				NULL, "</R>Printer Name: ",
				A_NORMAL,
				'_', vMIXED, 20, 2, 256, TRUE, FALSE);

         /* Set the printer name to the default printer. */
         defaultPrinter = getenv("PRINTER");
         setCDKEntry (entry, defaultPrinter, 2, 256, TRUE);
         printer = copyChar (activateCDKEntry (entry, NULL));
         destroyCDKEntry (entry);
 
         /* Print the group. */
         if (printGroup (groupList[x], "/tmp/rolodex.tmp", printer) == 0)
         {
            /* The group could not be printed. */
            sprintf (temp, "<C>Sorry the group '%s' could not be printed.", groupList[x].name);
            mesg[0] = strdup (temp);
            popupLabel (screen, mesg, 1);
            freeChar (mesg[0]);
         }

         /* Clean up. */
         destroyCDKLabel (title);
         freeChar (printer);
         unlink ("/tmp/rolodex.tmp");
      }
      else if (selectionList->selections[x] == 1)
      {
         /* Create a title. */
         sprintf (temp, "<C></R>Printing Group [%s] to File", groupList[x].name);
         mesg[0] = copyChar (temp);
         title = newCDKLabel (screen, CENTER, TOP, mesg, 1, FALSE, FALSE);
         drawCDKLabel (title, FALSE);
         freeChar (mesg[0]);

         /* Get the filename to print to. */
         entry = newCDKEntry (screen, CENTER, 8,
				NULL, "</R>Filename: ",
				A_NORMAL, '_', vMIXED,
				20, 2, 256, TRUE, FALSE);
         filename = copyChar (activateCDKEntry (entry, NULL));
         destroyCDKEntry (entry);

         /* Print the group. */
         if (printGroup (groupList[x], filename, printer) == 0)
         {
            /* The group could not be printed. */
            sprintf (temp, "<C>Sorry the group '%s' could not be printed.", groupList[x].name);
            mesg[0] = strdup (temp);
            popupLabel (screen, mesg, 1);
            freeChar (mesg[0]);
         }
         
         /* Clean up. */
         destroyCDKLabel (title);
         freeChar (filename);
      }
   }

   /* Clean up. */
   destroyCDKSelection (selectionList);
   for (x=0; x < groupCount; x++)
   {
      freeChar (itemList[x]);
   }
}

/*
 * This actually prints the phone record.
 */
int printGroup (SRolodex groupRecord, char *filename, char *printer)
{
   /* Declare local variables. */
   uid_t uid = getuid();
   char tempFilename[256], command[256];
   SPhoneData phoneData;
   SPhoneRecord *phoneRecord;
   int phoneCount, x;
   FILE *fd;

   /* Read the data file. */
   phoneCount = readPhoneDataFile (groupRecord.dbm, &phoneData);

   /* Create the temporary filename. */
   if (filename != (char *)NULL)
   {
      sprintf (tempFilename, "%s", filename);
   }
   else
   {
      sprintf (tempFilename, "/tmp/rolodex.%d", (int)uid);
   }

   /* Open the file. */
   if ((fd = fopen (tempFilename, "a+")) == NULL)
   {
      /* Clean up. */
      for (x=0; x < phoneCount; x++)
      {
         freeChar (phoneData.record[x].name);
         freeChar (phoneData.record[x].phoneNumber);
         freeChar (phoneData.record[x].address);
         freeChar (phoneData.record[x].city);
         freeChar (phoneData.record[x].province);
         freeChar (phoneData.record[x].postalCode);
         freeChar (phoneData.record[x].desc);
      }
      return 0;
   }

   /* Start writing the group information to the temp file. */
   fprintf (fd, "Group Name: %40s\n", groupRecord.name);
   fprintf (fd, "==============================================================================\n");
   for (x=0; x < phoneCount; x++)
   {
      phoneRecord = &phoneData.record[x];
      fprintf (fd, "Name        : %s\n", phoneRecord->name);
      fprintf (fd, "Phone Number: %s (%s)\n", phoneRecord->phoneNumber, GLineType[phoneRecord->lineType]);
      if (phoneRecord->lineType != vPager && phoneRecord->lineType != vCell)
      {
         fprintf (fd, "Address     : %-20s, %-20s\n", phoneRecord->address, phoneRecord->city);
         fprintf (fd, "            : %-10s, %-10s\n", phoneRecord->province, phoneRecord->postalCode);
      }
      fprintf (fd, "Description : %-30s\n", phoneRecord->desc);
      fprintf (fd, "------------------------------------------------------------------------------\n");
   }

   /* Determine if the information is going to a file or printer. */
   if (printer != (char *)NULL)
   {
      /* Print the file to the given printer. */
      sprintf (command, "lpr -P%s %s", printer, tempFilename);
      system (command);

      /* We have to unlkink the temp file. */
      unlink (tempFilename);
   }

   /* Clean up some memory. */
   for (x=0; x < phoneCount; x++)
   {
      freeChar (phoneData.record[x].name);
      freeChar (phoneData.record[x].phoneNumber);
      freeChar (phoneData.record[x].address);
      freeChar (phoneData.record[x].city);
      freeChar (phoneData.record[x].province);
      freeChar (phoneData.record[x].postalCode);
      freeChar (phoneData.record[x].desc);
   }

   /* Close the filename. */
   fclose (fd);
   return 1;
}

/*
 ****************************************************************
 *                  Start of callback functions.
 ****************************************************************
 */
int entryPreProcessCB (EObjectType cdkType, void *object, void *clientData, chtype input)
{
   if (input == ' ')
   {
      Beep();
      return 0;
   }
   return 1;
}

/*
 * This allows the user to insert a new phone entry into the database.
 */
int insertPhoneEntryCB (EObjectType cdkType, void *object, void *clientData, chtype key)
{
   /* Declare local variables. */
   CDKSCROLL *scroll= (CDKSCROLL *)object;
   SPhoneData *phoneData= (SPhoneData *)clientData;
   SPhoneRecord *phoneRecord= &phoneData->record[phoneData->recordCount];
   char temp[256];

   /* Make the scrolling list disappear. */
   eraseCDKScroll (scroll);

   /* Call the function which gets phone record information. */
   if (addPhoneRecord (scroll->screen, phoneData) == 0)
   {
      sprintf (temp, "%s (%s)", phoneRecord->name, GLineType[phoneRecord->lineType]);
      addCDKScrollItem (scroll, temp);
   }

   /* Redraw the scrolling list. */
   drawCDKScroll (scroll, scroll->box);
   return 0;
}

/*
 * This allows the user to delete a phone entry from the database.
 */
int deletePhoneEntryCB (EObjectType cdkType, void *object, void *clientData, chtype key)
{
   /* Declare local variables. */
   CDKSCROLL *scroll = (CDKSCROLL *)object;
   SPhoneData *phoneData = (SPhoneData *)clientData;
   char *mesg[3], temp[256], *hold;
   char *buttons[] = {"</B/16><No>", "</B/24><Yes>"};
   int position = scroll->currentItem;
   int x;

   /* Make the scrolling list disappear. */
   eraseCDKScroll (scroll);

   /* Check the number of entries left in the list. */
   if (scroll->listSize == 0)
   {
      mesg[0] = "There are no more numbers to delete.";
      popupLabel (scroll->screen, mesg, 1);
      return 0;
   }

   /* Ask the user if they really want to delete the listing. */
   mesg[0] = "<C>Do you really want to delete the phone entry";
   hold = chtype2Char (scroll->item[scroll->currentItem]);
   sprintf (temp, "<C></B/16>%s", hold);
   freeChar (hold);
   mesg[1] = copyChar (temp);
   if (popupDialog (scroll->screen, mesg, 2, buttons, 2) == 1)
   {
      /* Remove the item from the phone data record. */
      for (x=position; x < phoneData->recordCount-1; x++)
      {
         phoneData->record[x].name = phoneData->record[x+1].name;
         phoneData->record[x].lineType = phoneData->record[x+1].lineType;
         phoneData->record[x].phoneNumber = phoneData->record[x+1].phoneNumber;
         phoneData->record[x].address = phoneData->record[x+1].address;
         phoneData->record[x].city = phoneData->record[x+1].city;
         phoneData->record[x].province = phoneData->record[x+1].province;
         phoneData->record[x].postalCode = phoneData->record[x+1].postalCode;
         phoneData->record[x].desc = phoneData->record[x+1].desc;
      }
      phoneData->recordCount--;

      /* Nuke the entry. */
      deleteCDKScrollItem (scroll, position);
   }
   freeChar (mesg[1]);

   /* Redraw the scrolling list. */
   drawCDKScroll (scroll, scroll->box);
   return 0;
}

/*
 * This function provices help for the phone list editor.
 */
int phoneEntryHelpCB (EObjectType cdkType, void *object, void *clientData, chtype key)
{
   /* Declare local variables. */
   CDKSCROLL *scroll = (CDKSCROLL *)object;
   char *mesg[10], temp[100];

   /* Create the help title. */
   sprintf (temp, "<C></R>Rolodex Phone Editor");
   mesg[0] = copyChar (temp);

   sprintf (temp, "<B=i     > Inserts a new phone entry.");
   mesg[1] = copyChar (temp);

   sprintf (temp, "<B=d     > Deletes the currently selected phone entry.");
   mesg[2] = copyChar (temp);

   sprintf (temp, "<B=Escape> Exits the scrolling list.");
   mesg[3] = copyChar (temp);

   sprintf (temp, "<B=?     > Pops up this help window.");
   mesg[4] = copyChar (temp);

   /* Pop up the message. */
   popupLabel (scroll->screen, mesg, 5);

   /* Clean up. */
   freeChar (mesg[0]); freeChar (mesg[1]);
   freeChar (mesg[2]); freeChar (mesg[3]);
   freeChar (mesg[4]);
   return 0;
}

/*
 * This is a callback to the menu widget. It allows the user to 
 * ask for help about any sub-menu item.
 */
int helpCB (EObjectType cdkType, void *object, void *clientData, chtype key)
{
   /* Declare local variables. */
   CDKMENU *menu= (CDKMENU *)object;
   int menuList= menu->currentTitle;
   int submenuList= menu->currentSubtitle;
   int selection= ((menuList * 100) + submenuList);
   char *mesg[20], *name, temp[100];

   /* Create the help title. */
   name = chtype2Char (menu->sublist[menuList][submenuList]);
   stripWhiteSpace (vBOTH, name);
   sprintf (temp, "<C></R>Help<!R> </U>%s<!U>", name);
   mesg[0] = copyChar (temp);
   freeChar (name);

   /* Set the default value for the message. */
   mesg[1] = "<C>No help defined for this menu.";

   /* Given the current menu item, create a message. */
   if (selection == 0)
   {
      mesg[1] = "<C>This reads a new rolodex RC file.";
   }
   else if (selection == 1)
   {
      mesg[1] = "<C>This saves the current group information in the default RC file.";
   }
   else if (selection == 2)
   {
      mesg[1] = "<C>This saves the current group information in a new RC file.";
   }
   else if (selection == 3)
   {
      mesg[1] = "<C>This exits this program.";
   }
   else if (selection == 100)
   {
      mesg[1] = "<C>This creates a new rolodex group.";
   }
   else if (selection == 101)
   {
      mesg[1] = "<C>This opens a rolodex group.";
   }
   else if (selection == 102)
   {
      mesg[1] = "<C>This deletes a rolodex group.";
   }
   else if (selection == 200)
   {
      mesg[1] = "<C>This prints out selected groups phone numbers.";
   }
   else if (selection == 300)
   {
      mesg[1] = "<C>This gives a little history on this program.";
   }
   else if (selection == 301)
   {
      mesg[1] = "<C>This provides information about the rolodex.";
   }

   /* Pop up the message. */
   popupLabel (menu->screen, mesg, 2);
   freeChar (mesg[0]);

   /* Redraw the submenu window. */
   drawCDKMenuSubwin (menu);
   return 0;
}

/*
 * This is a callback to the group list scrolling list.
 */
int groupInfoCB (EObjectType cdkType, void *object, void *clientData, chtype key)
{
   /* Declare local variables. */
   CDKSCROLL *scroll= (CDKSCROLL *)object;
   SRolodex *groupList= (SRolodex *)clientData;
   int selection= scroll->currentItem;
   char *mesg[5], temp[100];

   /* Create the message to be displayed. */
   mesg[0] = "<C></U>Detailed Group Information.";

   sprintf (temp, "</R>Group Name         <!R> %s", groupList[selection].name);
   mesg[1] = copyChar (temp);

   sprintf (temp, "</R>Group Description  <!R> %s", groupList[selection].desc);
   mesg[2] = copyChar (temp);

   sprintf (temp, "</R>Group Database File<!R> %s", groupList[selection].dbm);
   mesg[3] = copyChar (temp);

   /* Displaye the message. */
   popupLabel (scroll->screen, mesg, 4);
   freeChar (mesg[1]); freeChar (mesg[2]);
   freeChar (mesg[3]);

   /* Redraw the scrolling list. */
   drawCDKScroll (scroll, scroll->box);
   return 0;
}
