// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: main.cpp 
// Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
// Produced By: Doug Gaer  
// File Creation Date: 09/18/1997  
// Date Last Modified: 03/18/1999
// Copyright (c) 1997 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
All those who put this code or its derivatives in a commercial
product MUST mention this copyright in their documentation for
users of the products in which this code or its derivative
classes are used. Otherwise, you have the freedom to redistribute
verbatim copies of this source code, adapt it to your specific
needs, or improve the code and release your improvements to the
public provided that the modified files carry prominent notices
stating that you changed the files and the date of any change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

The string database is a general purpose object-oriented
database. This version is designed to work with a terminal
interface.

This is a index card file program designed to work with 
a terminal interface.

- Modify "dbconfig.h" file to customize the database
- Modify "version.h" file to report changes and fixes

*/
// ----------------------------------------------------------- // 
#include <fstream.h> // Using ofstream to print to a text file
#include "ccidx_sh.h"
#include "terminal.h"
#include "config.h"
#include "timer.h"
#include "strutil.h"
#include "asprint.h"
#include "pscript.h"
#include "ccindex.h"
#include "dbconfig.h"
#include "version.h"
#include "vbdstats.h"
#include "btwalker.h"

static POD *DB;                // Global database pointer
const int KeyBuf = 25;         // Users input limit per string
const int CBuf = 81;           // Input limit for comments
const int StringOffset = 25;   // Display output limit per string
const int IntOffset = 8;       // Display output limit per number
const char *WildCard = "*";    // Used for wild card searches 
const int retrys = 3;          // Times to retry user input operations

// Define configurable parameters
int CacheSize = 15;  // Memory cache size for the index file
int AdminRights = 0; // Define user privileges 

// Define file access modes used in the application
VBDFile::AccessMode RWMode = VBDFile::READWRITE;
VBDFile::AccessMode ROMode = VBDFile::READONLY;

// Menu menu setup
typedef void (*MMF) (); // Pointer to (M)ain (M)enu (F)unctions
const int MMSelections = 6; // Main menu selections

// Main menu functions
void Exit();
void ISRMenu();     // Insertion/Removal menu
void FindMenu();    // Query menu
void DisplayMenu(); // Display menu
void FileMenu();    // File operations menu
void PrintMenu();   // Print menu

const char *mainmenu[MMSelections] = {
  "(0) - Exit this program",
  "(1) - Add, Remove, or Change items in the database",
  "(2) - Find items in the database",
  "(3) - Display items in the database",
  "(4) - File Operations",
  "(5) - Print Menu"
};

MMF MMFunctions[MMSelections] = {
  &Exit,
  &ISRMenu,
  &FindMenu,
  &DisplayMenu,
  &FileMenu,
  &PrintMenu
};

// Insertion/Removal menu setup
typedef void (*ISRMF) (); // Pointer to (A)dd (M)enu (F)unctions 
const char *ISRMTitle = "--------- DATABASE INSERTION/REMOVAL MENU ---------";
const int ISRMSelections = 4; // Insertion/Removal menu selections

// Insertion/Removal menu functions
void Return();
void Add();
void Change();
void Remove();

const char *isrmenu[ISRMSelections] = {
  "(0) - Return to main menu",
  "(1) - Add items to the database",
  "(2) - Change an item in the database",
  "(3) - Remove an item from the database"
};

ISRMF ISRMFunctions[ISRMSelections] = {
  &Return,
  &Add,
  &Change,
  &Remove
};

// Find menu setup
typedef void (*FMF) (); // Pointer to (F)ind (M)enu (F)unctions 
const char *FMTitle = "---------- DATABASE QUERY MENU ----------";
const int FMSelections = 12; // Find menu selections

// Find menu functions
void Return();
void FindByKeyName(); 
void FindByM1Name(); 
void FindByM2Name(); 
void FindByM3Name(); 
void FindByM4Name(); 
void FindByM5Name(); 
void FindByM6Name(); 
void FindByM7Name(); 
void FindByM8Name();
void FindByM9Name();
void FindByComments();

FMF FMFunctions[FMSelections] = {
  &Return,
  &FindByKeyName,
  &FindByM1Name,
  &FindByM2Name,
  &FindByM3Name,
  &FindByM4Name,
  &FindByM5Name,
  &FindByM6Name,
  &FindByM7Name,
  &FindByM8Name,
  &FindByM9Name,
  &FindByComments
};

// Display menu setup
typedef void (*DMF) (); // Pointer to (D)isplay (M)enu (F)unctions 
const char *DMTitle = "---------- DATABASE DISPLAY MENU ----------";
const int DMSelections = 2; // Display menu selections

// Display menu functions
void DisplayAll(); // Display all objects one at a time

const char *displaymenu[DMSelections] = {
  "(0) - Return to main menu",
  "(1) - Display all items one a time"
};

DMF DMFunctions[DMSelections] = {
  &Return,
  &DisplayAll
};

// File operations menu setup
typedef void (*FOMF) (); // Pointer to (F)ile (M)enu (F)unctions 
const char *FOMTitle = "---------- DATABASE FILE OPERATIONS MENU ----------";
const int FOMSelections = 9; // File operations menu selections

// File operations menu functions
void FileStats();
void ExportToASCII();
void ImportFromASCII();
void BackUp();
void Merge();
void CreateTemplate();
void CompareIndexFile();
void RebuildIndexFile();

const char *filemenu[FOMSelections] = {
  "(0) - Return to main menu",
  "(1) - Display Variable Block Database statistics",
  "(2) - Export to ASCII file (delimited by tabs)",
  "(3) - Import ASCII file (delimited by tabs)",
  "(4) - Backup the database to another file",
  "(5) - Merge the contents of another database file",
  "(6) - Create a template for an import file",
  "(7) - Compare the index file to the data file",
  "(8) - Rebuild the index file" 
};

FOMF FOMFunctions[FOMSelections] = {
  &Return,
  &FileStats,
  &ExportToASCII,
  &ImportFromASCII,
  &BackUp,
  &Merge,
  &CreateTemplate,
  &CompareIndexFile,
  &RebuildIndexFile
};

// Print menu setup
typedef void (*PMF) (); // Pointer to (P)rint (M)enu (F)unctions 
const char *PMTitle = "---------- DATABASE PRINTING MENU ----------";
const int PMSelections = 4; // Print menu selections

// Print menu functions
void ASCIIPrintAll();     // Print database to an ASCII file 
void ASCIIPrintObject();  // Print a single object to an ASCII file
void PostScriptPrint();   // Print database to a PostScript file

const char *printmenu[PMSelections] = {
  "(0) - Return to main menu",
  "(1) - Print database to text file", 
  "(2) - Print a single object to a text file",
  "(3) - Print to PostScript file"
};

PMF PMFunctions[PMSelections] = {
  &Return,
  &ASCIIPrintAll,
  &ASCIIPrintObject,
  &PostScriptPrint
};

// Function prototypes for functions use by all menu functions
char *ImportFormatwxMultiText(char *str);
void ExportPrintwxMultiText(char *str, ofstream &stream);
char *MultiLineInput(const char *title, Coords *p);
void DisplayMultiText(const char *title, char *str, Coords *p);
void ChangeObjectDisplay(CCIndex &ccindex, int adding, Coords *p);
void AddObject(CCIndex &ccindex, Coords *p);
void ChangeObject(CCIndex &ccindex, Coords *p, int adding = 0);
void DisplayObject(CCIndex &ccindex, Coords *p);
void DisplayString(const char *s);
void asPrintMultiText(char *title, char *str, ofstream &stream);
void asPrintText(char *title, char *str, ofstream &stream);
int LoadIndexKeys();

// PostScript printing constants and functions
const double psSEPARATOR = 2.0; // Spacing between data members

void PSPrintText(PostScriptDrv &psdrv, ofstream &stream, char *title,
		 char *str, int char_offset, int cell_len, double font_size,
		 PostScriptDrv::PSFonts item_font,
		 PostScriptDrv::PSFonts cell_font);

void PSPrintMultiText(PostScriptDrv &psdrv, ofstream &stream, char *title,
		      char *str, int char_offset, int cell_len,
		      double font_size, PostScriptDrv::PSFonts item_font,
		      PostScriptDrv::PSFonts cell_font);

// Search functions
void FindBy(const char *MemberName, CCIndexItem item);
void CCIndexDBSearch(CCIndex &ccindex, CCIndexItem item, UString &str,
		     Coords *p, const char *wildcard = 0);

void MainMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(MMTitle));
    p->SetY(terminal->ScreenCenter(MMSelections));
    terminal->ClearScreen();
    terminal->Write(MMTitle, p->XPos(), p->YPrev());

    for(int i = 0; i < MMSelections; i++)
      terminal->Write(mainmenu[i], p->XPos(), p->YOffset(1));
    
    terminal->Write("Press the number of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*MMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*MMFunctions[1]) (); // Call the appropriate menu function
	break;
      case '2':
	(*MMFunctions[2]) (); // Call the appropriate menu function
	break;
      case '3':
	(*MMFunctions[3]) (); // Call the appropriate menu function
	break;
      case '4':
	(*MMFunctions[4]) (); // Call the appropriate menu function
	break;
      case '5':
	(*MMFunctions[5]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void ISRMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(ISRMTitle));
    p->SetY(terminal->ScreenCenter(ISRMSelections));
    terminal->ClearScreen();
    terminal->Write(ISRMTitle, p->XPos(), p->YPrev());

    for(int i = 0; i < ISRMSelections; i++)
      terminal->Write(isrmenu[i], p->XPos(), p->YOffset(1));
    
    terminal->Write("Press the number of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*ISRMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*ISRMFunctions[1]) (); // Call the appropriate menu function
	break;
      case '2':
	(*ISRMFunctions[2]) (); // Call the appropriate menu function
	break;
      case '3':
	(*ISRMFunctions[3]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void FindMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(FMTitle));
    p->SetY(terminal->ScreenCenter(FMSelections));
    terminal->ClearScreen();
    terminal->Write(FMTitle, p->XPos(), p->YPrev());

    terminal->Write("(0) - Return to main menu", p->XPos(), p->YOffset(1));
    if(KeyName != 0) {
      terminal->Write("(1) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(KeyName);
    }
    if(M1Name != 0) {
      terminal->Write("(2) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M1Name);
    }
    if(M2Name != 0) {
      terminal->Write("(3) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M2Name);
    }
    if(M3Name != 0) {
      terminal->Write("(4) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M3Name);
    }
    if(M4Name != 0) {
      terminal->Write("(5) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M4Name);
    }
    if(M5Name != 0) {
      terminal->Write("(6) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M5Name);
    }
    if(M6Name != 0) {
      terminal->Write("(7) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M6Name);
    }
    if(M7Name != 0) {
      terminal->Write("(8) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M7Name);
    }
    if(M8Name != 0) {
      terminal->Write("(9) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M8Name);
    }
    if(M9Name != 0) {
      terminal->Write("(I) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M9Name);
    }
    if(Comments != 0) {
      terminal->Write("(C) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(Comments);
    }
    
    terminal->Write("Press the number/letter of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*FMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*FMFunctions[1]) (); // Call the appropriate menu function
	break;
      case '2':
	(*FMFunctions[2]) (); // Call the appropriate menu function
	break;
      case '3':
	(*FMFunctions[3]) (); // Call the appropriate menu function
	break;
      case '4':
	(*FMFunctions[4]) (); // Call the appropriate menu function
	break;
      case '5':
	(*FMFunctions[5]) (); // Call the appropriate menu function
	break;
      case '6':
	(*FMFunctions[6]) (); // Call the appropriate menu function
	break;
      case '7':
	(*FMFunctions[7]) (); // Call the appropriate menu function
	break;
      case '8':
	(*FMFunctions[8]) (); // Call the appropriate menu function
	break;
      case '9':
	(*FMFunctions[9]) (); // Call the appropriate menu function
	break;
      case 'i': case 'I': 
	(*FMFunctions[10]) (); // Call the appropriate menu function
	break;
      case 'c': case 'C':
	(*FMFunctions[11]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void DisplayMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(DMTitle));
    p->SetY(terminal->ScreenCenter(DMSelections));
    terminal->ClearScreen();
    terminal->Write(DMTitle, p->XPos(), p->YPrev());

    for(int i = 0; i < DMSelections; i++)
      terminal->Write(displaymenu[i], p->XPos(), p->YOffset(1));
    
    terminal->Write("Press the number of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*DMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*DMFunctions[1]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void FileMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(FOMTitle));
    p->SetY(terminal->ScreenCenter(FOMSelections));
    terminal->ClearScreen();
    terminal->Write(FOMTitle, p->XPos(), p->YPrev());

    for(int i = 0; i < FOMSelections; i++)
      terminal->Write(filemenu[i], p->XPos(), p->YOffset(1));
    
    terminal->Write("Press the number of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*FOMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*FOMFunctions[1]) (); // Call the appropriate menu function
	break;
      case '2':
	(*FOMFunctions[2]) (); // Call the appropriate menu function
	break;
      case '3':
	(*FOMFunctions[3]) (); // Call the appropriate menu function
	break;
      case '4':
	(*FOMFunctions[4]) (); // Call the appropriate menu function
	break;
      case '5':
	(*FOMFunctions[5]) (); // Call the appropriate menu function
	break;
      case '6':
	(*FOMFunctions[6]) (); // Call the appropriate menu function
	break;
      case '7':
	(*FOMFunctions[7]) (); // Call the appropriate menu function
	break;
      case '8':
	(*FOMFunctions[8]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void PrintMenu()
{
  int c;
  Coords *p = new Coords(0, 0);

  while(1) {
    p->SetXY(0, 0);
    p->SetX(terminal->Center(PMTitle));
    p->SetY(terminal->ScreenCenter(PMSelections));
    terminal->ClearScreen();
    terminal->Write(PMTitle, p->XPos(), p->YPrev());

    for(int i = 0; i < PMSelections; i++)
      terminal->Write(printmenu[i], p->XPos(), p->YOffset(1));
    
    terminal->Write("Press the number of your selection -> ",
		    p->XPos(), p->YOffset(2));
    
    c = terminal->GetChar();
    switch(c) {
      case '0': 
	(*PMFunctions[0]) (); // Call the appropriate menu function
	return;
      case '1':
	(*PMFunctions[1]) (); // Call the appropriate menu function
	break;
      case '2':
	(*PMFunctions[2]) (); // Call the appropriate menu function
	break;
      case '3':
	(*PMFunctions[3]) (); // Call the appropriate menu function
	break;
      default:
	break;
    }
  }
}

void Exit()
{
  // Nothing to do
}

void Return()
{
  // Nothing to do
}

int LoadIndexKeys()
{
  // Clear the doubly linked list
  dllist->Clear();

  BtreeWalker btw(DB->Index());
  unsigned num_objects = btw.Sort(LoadKeys);

  return num_objects;
}

void DisplayAll()
{
  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  CCIndex ccindex(DB);
  Coords *p = new Coords(0, 0);

  terminal->ClearScreen();      
  LoadIndexKeys();

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    terminal->ClearScreen();      
    p->SetXY(0, 0); // Reset x and y positions
    ccindex.ReadObject(dllistptr->Data.object_address);
    DisplayObject(ccindex, p);
    terminal->Write("(C)-Change, (D)-Delete, (X)-Exit",
		    p->XPos(), p->YOffset(2));
    terminal->Write(", Any other key to continue");
    int c = terminal->GetChar();
    switch(c) {
      case 'c': case 'C': 
	terminal->ClearScreen();
	p->SetXY(0, 0);
        ChangeObject(ccindex, p);
	break;
	      
      case 'd': case 'D': {
	int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
			     p->XPos(), p->YOffset(2));
	if(!yn) break;
        ccindex.DeleteObject();
	break;
      }
      
      case 'x': case 'X':
	dllist->Clear();
	delete p;
	return;
	      
      default:
	break;
    }
    dllistptr = dllistptr->GetNext(); 
  }

  dllist->Clear();
  delete p;
}

void Remove()
{
  if(KeyName == 0) return;

  if(!AdminRights) {
    terminal->ClearScreen();
    terminal->Write("You do not have Admin User Privileges", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }
  
  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);
  char buf[KeyBuf]; 

  terminal->Write(KeyName, p->XPos(), p->YPos());
  terminal->Write(" to delete -> ");
  terminal->GetString(buf);

  CCIndex ccindex(DB);
  ccindex.SetCardName(buf);

  FAU addr = ccindex.FindObject();
   
  if(!addr) { 
    terminal->Write("Could not find: ", p->XPos(), p->YOffset(2));
    terminal->Write(buf);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coord pointer
    return;
  }
  int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
			   p->XPos(), p->YOffset(2));
  if(!yn) {
    delete p; // Free Coord pointer
    return;
  }
  
  ccindex.DeleteObject();
  terminal->Write("Deleted entry for: ", p->XPos(), p->YOffset(2));
  terminal->Write(buf);
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p; // Free Coord pointer
}

void FileStats()
{
  VBDStats(terminal, DB->OpenDatabase());
  VBDStats(terminal, DB->OpenIndexFile());
}

void PrintItemBar(ofstream &stream, int abv)
{

  int i = 0;
  if(KeyName != 0) ASPrint(KeyName, stream, StringOffset+1);
  if(M2Name != 0) ASPrint(M2Name, stream, StringOffset+1);
  if(M3Name != 0) ASPrint(M3Name, stream, StringOffset+1);
  if(M4Name != 0) ASPrint(M4Name, stream, StringOffset+1);
  if(abv) { // Print abbreviated list for portrait print outs
    if(M5Name != 0) ASPrint(M5Name, stream, StringOffset);
    stream << asLineFeed;
    i = 0;
    while (i++ < asPrintCols-1) stream << '=';
    stream << asLineFeed;
    return;
  }
  else { // Print full listing for landscape print outs
    if(M5Name != 0) ASPrint(M5Name, stream, StringOffset+1);
    if(M6Name != 0) ASPrint(M6Name, stream, StringOffset+1);
    if(M7Name != 0) ASPrint(M7Name, stream, StringOffset+1);
    if(M8Name != 0) ASPrint(M8Name, stream, StringOffset);
    stream << asLineFeed;
    i = 0;
    while (i++ < asPrintColsLong-1) stream << '=';
    stream << asLineFeed;
  }
}

void ASCIIPrintAll()
{
  char buf[CBuf];
  char *FileName;
  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  terminal->Write("Enter file name to print to: ",
		  p->XPos(), p->YPos());
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(2));
    if(!yn) {
      delete p;
      delete[] tmp;
      return;
    }
  }

  FileName = tmp;
  delete[] tmp;
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  terminal->Write("Printing to: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coords pointer
    return; 
  }

  terminal->Write("Printing...", p->XPos(), p->YOffset(2));
  CCIndex ccindex(DB);

  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.",
		    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(1));
    stream.close();
    delete p;
    return;
  }

  LoadIndexKeys();

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    asPrintText((char *)KeyName, ccindex.GetCardName(), stream);
    asPrintText((char *)M1Name, ccindex.GetCompanyName(), stream);
    asPrintText((char *)M2Name, ccindex.GetDepartmentName(), stream);
    asPrintText((char *)M3Name, ccindex.GetPhoneNumber(), stream);
    asPrintText((char *)M4Name, ccindex.GetFaxNumber(), stream);
    asPrintText((char *)M5Name, ccindex.GetCellNumber(), stream);
    asPrintText((char *)M6Name, ccindex.GetBeeperNumber(), stream);
    asPrintText((char *)M7Name, ccindex.GetEmailAddress(), stream);
    asPrintMultiText((char *)M8Name, ccindex.GetStreetAddress(), stream);
    asPrintMultiText((char *)M9Name, ccindex.GetInternetURLS(), stream);
    asPrintMultiText((char *)Comments, ccindex.GetCommentsBlock(), stream);
    stream << asLineFeed;
    dllistptr = dllistptr->GetNext(); 
  }

  dllist->Clear(); 
  terminal->Write("Finished", p->XPos(), p->YOffset(2));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  stream.close();
}


void ASCIIPrintObject()
{
  if(KeyName == 0) return;
  
  Coords *p = new Coords(0, 0);
  char *FileName;
  char buf[KeyBuf];
  
  terminal->ClearScreen();
  terminal->Write(KeyName, p->XPos(), p->YPos());
  terminal->Write(" to print -> ");
  terminal->GetString(buf);

  CCIndex ccindex(DB);
  ccindex.SetCardName(buf);

  FAU addr = ccindex.FindObject();
   
  if(!addr) { 
    terminal->Write("Could not find: ", p->XPos(), p->YOffset(2));
    terminal->Write(buf);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free Coords pointer
    return;
  }

  terminal->Write("Enter file name to print to: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(2));
    if(!yn) {
      delete p;
      delete[] tmp;
      return;
    }
  }

  FileName = tmp;
  delete[] tmp;
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  terminal->Write("Printing to: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coords pointer
    return; 
  }

  terminal->Write("Printing...", p->XPos(), p->YOffset(2));

  asPrintText((char *)KeyName, ccindex.GetCardName(), stream);
  asPrintText((char *)M1Name, ccindex.GetCompanyName(), stream);
  asPrintText((char *)M2Name, ccindex.GetDepartmentName(), stream);
  asPrintText((char *)M3Name, ccindex.GetPhoneNumber(), stream);
  asPrintText((char *)M4Name, ccindex.GetFaxNumber(), stream);
  asPrintText((char *)M5Name, ccindex.GetCellNumber(), stream);
  asPrintText((char *)M6Name, ccindex.GetBeeperNumber(), stream);
  asPrintText((char *)M7Name, ccindex.GetEmailAddress(), stream);
  asPrintMultiText((char *)M8Name, ccindex.GetStreetAddress(), stream);
  asPrintMultiText((char *)M9Name, ccindex.GetInternetURLS(), stream);
  asPrintMultiText((char *)Comments, ccindex.GetCommentsBlock(), stream);
  terminal->Write("Finished", p->XPos(), p->YOffset(2));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p; // Free Coords pointer
}

void asPrintText(char *title, char *str, ofstream &stream)
// Print a single line of a wxText item to a ASCII File  
{
  ASPrint(title, stream, StringOffset+1);
  ASPrint(str, stream, strlen(str));
  stream << asLineFeed;
}

void asPrintMultiText(char *title, char *str, ofstream &stream)
// Prints four lines of a wxMultiText box to a ACSII file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;

  ASPrint(title, stream, StringOffset+1);
  
  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
  
  parse(str, words, &num, dchar);

  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length());
  }

  stream << asLineFeed;

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }

  stream << asLineFeed;
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }
  
  stream << asLineFeed;
  
  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), stream, line_buf.length(), StringOffset+1);
  }
  stream << asLineFeed;
}

void Add()
{
  if(!AdminRights) {
    terminal->ClearScreen();
    terminal->Write("You do not have Admin User Privileges", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  int r = retrys;
  
  Coords *p = new Coords(0, 0);
  char buf[KeyBuf];
  terminal->ClearScreen();

  while(1) {
    terminal->Write(KeyName, p->XPos(), p->YPos());
    terminal->Write(" to add -> ");
    terminal->GetString(buf);
    if(!*buf) r--; else break;
    if(!r) {
      delete p;
      return;
    }
  }
  
  CCIndex ccindex(DB);
  UString key_buf(buf);

  // Remove any leading space from the key name entry
  int offset = key_buf.Find(" ");
  if(offset == 0) key_buf.DeleteAt(offset, 1);
    
  if(key_buf == "") { // Check for a key name valid input
    delete p;
    return;
  }

  ccindex.SetCardName(key_buf);

  FAU addr = ccindex.FindObject();
   
  if(addr) { 
    terminal->Write(buf, p->XPos(), p->YOffset(2));
    terminal->Write(" entry already exists."); 
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free Coords pointer
    return;
  }

  AddObject(ccindex, p);
  delete p; // Free Coords pointer
}

void AddObject(CCIndex &ccindex, Coords *p)
{
  char buf[KeyBuf];

  terminal->Write(M1Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetCompanyName(buf);

  terminal->Write(M2Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetDepartmentName(buf);
  
  terminal->Write(M3Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetPhoneNumber(buf);

  terminal->Write(M4Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetFaxNumber(buf);

  terminal->Write(M5Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetCellNumber(buf);

  terminal->Write(M6Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetBeeperNumber(buf);

  terminal->Write(M7Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  ccindex.SetEmailAddress(buf);

  ccindex.SetStreetAddress(MultiLineInput(M8Name, p));
  ccindex.SetInternetURLS(MultiLineInput(M9Name, p));
  ccindex.SetCommentsBlock(MultiLineInput(Comments, p));
  
  terminal->Write("(A)-Abort (C)-Change, Any other key to accept entry",
		  p->XPos(), p->YOffset(2));
  int c = terminal->GetChar();
  switch(c) {
    case 'c': case 'C':
      terminal->ClearScreen();
      p->SetXY(0, 0);
      ChangeObject(ccindex, p, 1); 
      terminal->ClearScreen();
      p->SetXY(0, 0); // Reset the screen coords
      break;
    case 'a': case 'A':
      return;
    default:
      break;
  }

  FAU addr = ccindex.AddObject(0); // Write the object to the file
  if(!addr) {
    terminal->Write("Could not add item to database",
                    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    return;
  }
  
  terminal->Write("Item was added to the database",
                  p->XPos(), p->YOffset(2));

  terminal->Write("Add another (y/n)", p->XPos(), p->YOffset(2));
  if(terminal->GetYesNo()) Add(); else return; 
}

char *MultiLineInput(const char *title, Coords *p)
{
  char buf[KeyBuf];
  UString title_line(title);
  title_line += " LINE 1";
  terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  if(!*buf) return "\0";
  UString line_buf(buf);
  int offset = title_line.Find("1");
  title_line.DeleteAt(offset, 1);
  title_line.InsertAt(offset, "2");
  terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  if(!*buf) return line_buf.c_str(); else line_buf += "\n";
  line_buf += buf;
  offset = title_line.Find("2");
  title_line.DeleteAt(offset, 1);
  title_line.InsertAt(offset, "3");
  terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  if(!*buf) return line_buf.c_str(); else line_buf += "\n";
  line_buf += buf;
  offset = title_line.Find("3");
  title_line.DeleteAt(offset, 1);
  title_line.InsertAt(offset, "4");
  terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  if(!*buf) return line_buf.c_str(); else line_buf += "\n";
  line_buf += buf;
  return line_buf.c_str();
}

void FindBy(const char *MemberName, CCIndexItem item)
{  
  if(MemberName == 0) return;

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);
  char buf[KeyBuf];
  unsigned offset = 0;
  
  CCIndex ccindex(DB);

  terminal->Write("Enter complete string or use ", p->XPos(), p->YPos());
  terminal->Write(WildCard);
  terminal->Write(" for a wild card.");
  terminal->Write(MemberName, p->XPos(), p->YOffset(2));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  UString str(buf);
  offset = str.Find(WildCard, offset); // Look for wild card character

  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.",
		    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(1));
    delete p;
    return;
  }
  
  if(offset == UString::NoMatch) { // No wild cards found
    ccindex.SetCardName(buf);
    CCIndexDBSearch(ccindex, item, str, p);
    delete p; // Free the Coord pointer
    return;
  }

  CCIndexDBSearch(ccindex, item, str, p, WildCard);
  delete p; // Free the Coord pointer
}

void FindByKeyName()
{
  FindBy(KeyName, CARD_NAME);
}

void FindByM1Name()
{
  FindBy(M1Name, COMPANY_NAME);
}

void FindByM2Name()
{
  FindBy(M2Name, DEPARTMENT_NAME);
}

void FindByM3Name()
{
  FindBy(M3Name, PHONE_NUMBER);
}

void FindByM4Name()
{
  FindBy(M4Name, FAX_NUMBER);
}

void FindByM5Name()
{
  FindBy(M5Name, CELL_NUMBER);
}

void FindByM6Name()
{
  FindBy(M6Name, BEEPER_NUMBER);
}

void FindByM7Name()
{
  FindBy(M7Name, EMAIL_ADDRESS);
}

void FindByM8Name()
{
  FindBy(M8Name, STREET_ADDRESS);
}

void FindByM9Name()
{
  FindBy(M9Name, INTERNET_URLS);
}

void FindByComments()
{
  FindBy(Comments, COMMENTS_BLOCK);
}

void Change()
{
  if(KeyName == 0) return;

  if(!AdminRights) {
    terminal->ClearScreen();
    terminal->Write("You do not have Admin User Privileges", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  Coords *p = new Coords(0, 0);
  char buf[KeyBuf];
  
  terminal->ClearScreen();
  terminal->Write(KeyName, p->XPos(), p->YPos());
  terminal->Write(" to change -> ");
  terminal->GetString(buf);

  CCIndex ccindex(DB);
  ccindex.SetCardName(buf);

  FAU addr = ccindex.FindObject();
   
  if(!addr) { 
    terminal->Write("Could not find: ", p->XPos(), p->YOffset(2));
    terminal->Write(buf);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free Coords pointer
    return;
  }

  ChangeObject(ccindex, p);
  delete p; // Free Coords pointer
}

void ChangeObject(CCIndex &ccindex, Coords *p, int adding)
{
  char buf[KeyBuf];
  
  // Buffer the object's data to allow changes to be canceled
  CCIndex ob;
  ob.Copy(ccindex);
  FAU addr;
  CCIndex changed(DB); // Temp buffer used to record changes
  char *prompt = "Press the number/letter of your selection -> ";  
  int c, x, y, x_prev, y_prev, x_prompt, y_prompt;
  x_prev = p->XPos(); y_prev = p->YPos();

  while(1) {
    ChangeObjectDisplay(ccindex, adding, p);    
    terminal->Write(prompt, p->XPos(), p->YOffset(2));
    x_prompt = p->XPos() + strlen(prompt);
    y_prompt = p->YPos();
    x = p->XPos();
    y = p->YPos() + 2;
    
    c = terminal->GetChar();
    switch(c) {
      case 'C': case 'c': 
        ccindex.Copy(ob);
	return;

      case '0':
	// User not allowed to change name unless adding a new object
	if(!adding) break; 
	if(KeyName != 0) { 
	  terminal->ClearScreen();
	  terminal->Write(KeyName, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
          ccindex.SetCardName(buf);
          addr = ccindex.FindObject();
   	  if(addr) { 
	    terminal->Write("Entry already exists!", x, y+1);
	    terminal->AnyKey();
	    terminal->ClearLine(x, y+1);
	    terminal->ClearLine(x, y);
	    terminal->MoveCursor(x_prev, y_prev);
	    break;
	  }
          ccindex.SetCardName(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '1':
	terminal->ClearScreen();
	terminal->Write(M1Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetCompanyName(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '2':
	terminal->ClearScreen();
	terminal->Write(M2Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetDepartmentName(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;
	
      case '3':
	terminal->ClearScreen();
	terminal->Write(M3Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetPhoneNumber(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '4':
	terminal->ClearScreen();
	terminal->Write(M4Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetFaxNumber(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '5':
	terminal->ClearScreen();
	terminal->Write(M5Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetCellNumber(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '6':
	terminal->ClearScreen();
	terminal->Write(M6Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetBeeperNumber(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '7':
	terminal->ClearScreen();
	terminal->Write(M7Name, 2, 1);
	terminal->Write(" -> ");
	terminal->GetString(buf);
        ccindex.SetEmailAddress(buf);
        ChangeObjectDisplay(ccindex, adding, p);
	break;

      case '8':
	terminal->ClearScreen();
	p->SetXY(2, 1);
        ccindex.SetStreetAddress(MultiLineInput(M8Name, p));
	break;

      case '9':
	terminal->ClearScreen();
	p->SetXY(2, 1);
        ccindex.SetInternetURLS(MultiLineInput(M9Name, p));
	break;

      case 'M': case 'm':
	terminal->ClearScreen();
	p->SetXY(2, 1);
        ccindex.SetCommentsBlock(MultiLineInput(Comments, p));
	break;
	
      case 'A': case 'a':
	if(adding) return; // New object, no need to reallocate space
        if(ccindex.FullCompare(ob)) return; // The object has not changed
        changed.Copy(ccindex);  // Make a copy of the changes

	// Remove the original object 
        ccindex.DeleteObject(); 
	changed.AddObject(0); // Add the changed object back to the database
	return;
	
      default:
	break;
    }
  }
}

void ChangeObjectDisplay(CCIndex &ccindex, int adding, Coords *p)
{
  UString title_buf;  
  terminal->ClearScreen();
  p->SetXY(0, 0);

  terminal->Write("(C) Cancel change", p->XPos(), p->YOffset(1));
  if(adding) {
    terminal->Write("(0) ", p->XPos(), p->YOffset(1));
    terminal->Write(KeyName); terminal->Write(" = ");
    terminal->Write(ccindex.GetCardName());
  } 
  else {
    terminal->Write("(-) ", p->XPos(), p->YOffset(1));
    terminal->Write(KeyName); terminal->Write(" = ");
    terminal->Write(ccindex.GetCardName());
  }
  
  terminal->Write("(1) ", p->XPos(), p->YOffset(1));
  terminal->Write(M1Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetCompanyName());
  
  terminal->Write("(2) ", p->XPos(), p->YOffset(1));
  terminal->Write(M2Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetDepartmentName());
  
  terminal->Write("(3) ", p->XPos(), p->YOffset(1));
  terminal->Write(M3Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetPhoneNumber());
  
  terminal->Write("(4) ", p->XPos(), p->YOffset(1));
  terminal->Write(M4Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetFaxNumber());
  terminal->ClearLine();
  
  terminal->Write("(5) ", p->XPos(), p->YOffset(1));
  terminal->Write(M5Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetCellNumber());
  
  terminal->Write("(6) ", p->XPos(), p->YOffset(1));
  terminal->Write(M6Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetBeeperNumber());
  
  terminal->Write("(7) ", p->XPos(), p->YOffset(1));
  terminal->Write(M7Name); terminal->Write(" = ");
  terminal->Write(ccindex.GetEmailAddress());
  
  title_buf = "(8) "; title_buf += M8Name;
  DisplayMultiText(title_buf.c_str(), ccindex.GetStreetAddress(), p);
  title_buf = "(9) "; title_buf += M9Name;
  DisplayMultiText(title_buf.c_str(), ccindex.GetInternetURLS(), p);
  title_buf = "(M) "; title_buf += Comments;
  DisplayMultiText(title_buf.c_str(), ccindex.GetCommentsBlock(), p);
  
  terminal->Write("(A) Accept changes", p->XPos(), p->YOffset(1));
}

void DisplayObject(CCIndex &ccindex, Coords *p)
// Display's the complete object with all details.
{
  terminal->Write(KeyName, p->XPos(), p->YPos());
  terminal->Write(" = ");
  terminal->Write(ccindex.GetCardName());

  terminal->Write(M1Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetCompanyName());

  terminal->Write(M2Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetDepartmentName());
  
  terminal->Write(M3Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetPhoneNumber());
  
  terminal->Write(M4Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetFaxNumber());
  
  terminal->Write(M5Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetCellNumber());

  terminal->Write(M6Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetBeeperNumber());

  terminal->Write(M7Name, p->XPos(), p->YOffset(1));
  terminal->Write(" = ");
  terminal->Write(ccindex.GetEmailAddress());

  DisplayMultiText(M8Name, ccindex.GetStreetAddress(), p);
  DisplayMultiText(M9Name, ccindex.GetInternetURLS(), p);
  DisplayMultiText(Comments, ccindex.GetCommentsBlock(), p);
}

void DisplayMultiText(const char *title, char *str, Coords *p)
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num, offset, i, j;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;

  UString title_line(title);
  title_line += " LINE 1 = ";
  terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
  
  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
  
  parse(str, words, &num, dchar);

  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    terminal->Write(words[0]);
  }
  else
    return;

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    offset = title_line.Find("1");
    title_line.DeleteAt(offset, 1);
    title_line.InsertAt(offset, "2");
    terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
    terminal->Write(words[1]);
  }
  else
    return;
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    offset = title_line.Find("2");
    title_line.DeleteAt(offset, 1);
    title_line.InsertAt(offset, "3");
    terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
    terminal->Write(words[2]);
  }
  else
    return;
  
  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    offset = title_line.Find("3");
    title_line.DeleteAt(offset, 1);
    title_line.InsertAt(offset, "4");
    terminal->Write(title_line.c_str(), p->XPos(), p->YOffset(1));
    terminal->Write(words[3]);
  }
}

void DisplayString(const char *s)
{
  int len = strlen(s);
  int offset = StringOffset;
  char *buf = new char[len + 1];
  buf[len] = '\0';
  strcpy(buf, s);
  if(len < StringOffset) offset = len;
  for(int i = 0; i < offset; i++)
    terminal->Write(buf[i]);
  delete[] buf;
}

void Version()
{
  printf("\n%s program version %.3f\n", ProgramName, VersionNumber);
  exit(0);
}

void ExportToASCII()
{
  char buf[CBuf];
  char *FileName;
  const char dchar = '\t';  // Text delimiter
  const char filter = '\t'; // Filter out tabs
  const char *Fill = "Unknown"; // Fill character string

  terminal->ClearScreen();

  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  Coords *p = new Coords(0, 0);

  terminal->Write("Export database to ASCII file delimited by tabs",
		  p->XPos(), p->YPos());

  terminal->Write("Enter file name to export to: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(2));
    if(!yn) {
      delete p;
      delete[] tmp;
      return;
    }
  }

  FileName = tmp;
  delete[] tmp;
  ofstream stream(FileName, ios::out); // Open file and truncate it
  terminal->Write("Exporting database to: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coords pointer
    return; 
  }

  CCIndex ccindex(DB);

  // Write the item bar to the file first
  ASPrint(KeyName, stream, strlen(KeyName));
  stream << dchar;

  ASPrint(M1Name, stream, strlen(M1Name));
  stream << dchar;

  ASPrint(M2Name, stream, strlen(M2Name));
  stream << dchar;

  ASPrint(M3Name, stream, strlen(M3Name));
  stream << dchar;

  ASPrint(M4Name, stream, strlen(M4Name));
  stream << dchar;

  ASPrint(M5Name, stream, strlen(M5Name));
  stream << dchar;

  ASPrint(M6Name, stream, strlen(M6Name));
  stream << dchar;

  ASPrint(M7Name, stream, strlen(M7Name));
  stream << dchar;
  
  ASPrint(M8Name, stream, strlen(M8Name));
  stream << dchar;
  
  ASPrint(M9Name, stream, strlen(M9Name));
  stream << dchar;

  ASPrint(Comments, stream, strlen(Comments));
  stream << asLineFeed;
  
  int exports = 0;

  LoadIndexKeys();

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);
    ASPrint(ccindex.GetCardName(), filter, stream,
	    strlen(ccindex.GetCardName()));
    stream << dchar;
    
    if(!*ccindex.GetCompanyName())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetCompanyName(), filter, stream,
              strlen(ccindex.GetCompanyName()));
    stream << dchar;

    if(!*ccindex.GetDepartmentName())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetDepartmentName(), filter, stream,
              strlen(ccindex.GetDepartmentName()));
      stream << dchar;

      if(!*ccindex.GetPhoneNumber())
      ASPrint(Fill, stream, strlen(Fill));
    else
      ASPrint(ccindex.GetPhoneNumber(), filter, stream,
              strlen(ccindex.GetPhoneNumber()));
      stream << dchar;

      if(!*ccindex.GetFaxNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetFaxNumber(), filter, stream,
                strlen(ccindex.GetFaxNumber()));
      stream << dchar;

      if(!*ccindex.GetCellNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetCellNumber(), filter, stream,
                strlen(ccindex.GetCellNumber()));
      stream << dchar;

      if(!*ccindex.GetBeeperNumber())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetBeeperNumber(), filter, stream,
                strlen(ccindex.GetBeeperNumber()));
      stream << dchar;

      if(!*ccindex.GetEmailAddress())
	ASPrint(Fill, stream, strlen(Fill));
      else
        ASPrint(ccindex.GetEmailAddress(), filter, stream,
                strlen(ccindex.GetEmailAddress()));
      stream << dchar;

      if(!*ccindex.GetStreetAddress())
	ASPrint(Fill, stream, strlen(Fill));
      else 
        ExportPrintwxMultiText(ccindex.GetStreetAddress(), stream);
      stream << dchar;
    
      if(!*ccindex.GetInternetURLS())
	ASPrint(Fill, stream, strlen(Fill));
      else 
        ExportPrintwxMultiText(ccindex.GetInternetURLS(), stream);
      stream << dchar;

      if(!*ccindex.GetCommentsBlock())
	ASPrint("None", stream, strlen("None"));
      else 
        ExportPrintwxMultiText(ccindex.GetCommentsBlock(), stream);

    stream << asLineFeed;
    exports++;
    dllistptr = dllistptr->GetNext(); 
  }

  dllist->Clear(); 
  terminal->Write("Exported ", p->XPos(), p->YOffset(2));
  terminal->Write(exports);
  terminal->Write(" items.");
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p;          // Free the Coords pointer
  stream.close();
}

void ExportPrintwxMultiText(char *str, ofstream &stream)
// Prints four lines of a wxMultiText box to a ASCII export file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char filter = '\t'; // Filter out tabs
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;

  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
  
  parse(str, words, &num, dchar);

  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }

  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }
  
  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    stream << "\\n"; 
    ASPrint(line_buf.c_str(), filter, stream, line_buf.length());
  }
}

void ImportFromASCII()
{
  if(KeyName == 0) return;

  if(!AdminRights) {
    terminal->ClearScreen();
    terminal->Write("You do not have Admin User Privileges", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  char buf[CBuf];
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  const int MaxLine = 512;
  char LineBuffer[MaxLine];
  char *sbuf;
  char *FileName;
  const char dchar = '\t';  // Text delimiter

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  terminal->Write("Importing database from ASCII file delimited by tabs",
		  p->XPos(), p->YPos());

  terminal->Write("Enter file name to import from: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);
  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(!VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" does not exists.");
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }

  FileName = tmp;
  ifstream stream(FileName, ios::in|ios::nocreate);
  terminal->Write("Importing database from: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not open file: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete[] tmp;
    delete p; // Free the Coords pointer
    return; 
  }

  terminal->Write("Importing...", p->XPos(), p->YOffset(2));

  ChSNode *listptr; // Linked list node pointer
  ChSList list;     // Linked list
  
  while(!stream.eof())
  {
    // Read in file line by line
    stream.getline(LineBuffer, MaxLine);

    // Allocate memory for temporary holding buffer
    sbuf = new char[strlen(LineBuffer)+1];
    
    // Copy contents of the array to temporary holding buffer
    strcpy(sbuf, LineBuffer);

    // Load file data into singly-linked list
    listptr = list.Store(sbuf); 

    if(!listptr) { // Out of memory
      stream.close();
      delete[] sbuf;
      list.Clear();
      return;
   }
  }
  stream.close();
  delete[] sbuf;
  
  CCIndex ccindex(DB);
  CCIndex ccindexa(DB);
  CCIndex ob(DB);
  
  int linecount = 0;
  int imports = 0;
  int updates = 0;
  int updateall = 0;
  int ObjectItems = 11; // Number of class data members 
  int x_prev, y_prev, c;
  FAU addr;
  
  listptr = list.GetFront();
  while(!list.IsHeader(listptr)) {
    if(parse(listptr->Data, words, &num, dchar) == 1) {
      terminal->Write("Parse error!", p->XPos(), p->YOffset(2));
      break;
    }

    linecount++;
    if(num != ObjectItems && (num != 0 && num != 1)) {
      x_prev = p->XPos(); y_prev = p->YPos();
      terminal->Write("Error in ", p->XPos(), p->YOffset(2));
      terminal->Write(FileName);
      terminal->Write(" import file!");
      if(num > ObjectItems) 
	terminal->Write("To many items on line: ", p->XPos(), p->YOffset(2));
      if(num < ObjectItems) 
	terminal->Write("Not enough items on line: ",
			p->XPos(), p->YOffset(2));
      terminal->Write(linecount);
      terminal->Write("(X)-Exit, Any other key to continue",
		    p->XPos(), p->YOffset(2));
      c = terminal->GetChar();
      switch(c) {
	case 'x': case 'X':
	  delete p;          // Free the Coords pointer
	  delete[] tmp;
	  list.Clear();
	  return;
	default:
	  break;
      }
      terminal->ClearLine(p->XPos(), p->YPos());
      terminal->ClearLine(p->XPos(), p->YPos()-4);
      terminal->ClearLine(p->XPos(), p->YPos()-2);
      p->SetXY(x_prev, y_prev);
    }

    if(num == ObjectItems && *words[0] != 0) {
      ccindex.SetCardName(words[0]);
      ccindex.SetCompanyName(words[1]);
      ccindex.SetDepartmentName(words[2]);
      ccindex.SetPhoneNumber(words[3]);
      ccindex.SetFaxNumber(words[4]);
      ccindex.SetCellNumber(words[5]);
      ccindex.SetBeeperNumber(words[6]);
      ccindex.SetEmailAddress(words[7]);
      ccindex.SetStreetAddress(ImportFormatwxMultiText(words[8]));
      ccindex.SetInternetURLS(ImportFormatwxMultiText(words[9]));
      ccindex.SetCommentsBlock(ImportFormatwxMultiText(words[10]));

      ccindexa.SetCardName(ccindex.GetCardName());
      addr = ccindexa.FindObject();

      if(addr) { 
	x_prev = p->XPos(); y_prev = p->YPos();
	if(updateall == 0) {
          terminal->Write(ccindex.GetCardName(), p->XPos(), p->YOffset(2));
	  terminal->Write(" entry already exists."); 
	  terminal->Write("(U)-Update, (A)-Update all, (X)-Exit",
			  p->XPos(), p->YOffset(2));
	  terminal->Write(", Any other key to continue");
	  c = terminal->GetChar();
	}
	if(updateall == 1) c = 'a';
	switch(c) {
	  case 'a': case 'A':
	    updateall = 1;
	    updates++;
            ob.Copy(ccindexa);
            if(ccindex.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            ccindex.AddObject(0);
	    break;
	  case 'u': case 'U': 
	    updates++;
            ob.Copy(ccindexa);
            if(ccindex.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject();
            ccindex.AddObject(0);
 	    break;
	  case 'x': case 'X':
	    delete p;          // Free the Coords pointer
	    delete[] tmp;
	    list.Clear();
	    return;
	  default:
	    break;
	}
	if(updateall == 1) {
	  p->SetXY(x_prev, y_prev);
	  terminal->ClearLine(x_prev, y_prev+2);
	  terminal->ClearLine(x_prev, y_prev+4);
	  terminal->Write("Updating entry for: ", p->XPos(), p->YOffset(2)); 
          terminal->Write(ccindex.GetCardName(), p->XPos(), p->YOffset(2)); 
	  Sleep((clock_t)1);
	  terminal->ClearLine(p->XPos(), p->YPos());
	  terminal->ClearLine(p->XPos(), p->YPos()-2);
	  p->SetXY(x_prev, y_prev);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	else {
	  terminal->ClearLine(p->XPos(), p->YPos());
	  terminal->ClearLine(p->XPos(), p->YPos()-2);
	  p->SetXY(x_prev, y_prev);
	  terminal->MoveCursor(x_prev, y_prev);
	}
      }
      else {
	// Ensure that the template does not get added to the file
	if(CaseICmp(KeyName, words[0]) != 0) {
          FAU addr = ccindex.AddObject(0); // Write the object to the file
	  imports++;
	  if(!addr) {
	    x_prev = p->XPos(); y_prev = p->YPos();
	    terminal->Write("Could not add:", p->XPos(), p->YOffset(2));
            terminal->Write(ccindex.GetCardName());
	    terminal->Write(" to database");
	    terminal->AnyKey(p->XPos(), p->YOffset(2));
	    terminal->ClearLine(p->XPos(), p->YPos());
	    terminal->ClearLine(p->XPos(), p->YPos()-2);
	    p->SetXY(x_prev, y_prev);
	    imports--;
	  }
	}
      }
    }
    listptr = listptr->GetNext();
  }

  terminal->Write("Imported ", p->XPos(), p->YOffset(2));
  terminal->Write(imports);
  terminal->Write(" items.");
  if(updates) {
    terminal->Write("Updated ", p->XPos(), p->YOffset(2));
    terminal->Write(updates);
    terminal->Write(" items.");
  }
  
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p;          // Free the Coords pointer
  delete[] tmp;
  list.Clear();
}

char *ImportFormatwxMultiText(char *str)
{
  UString line_buf(str);
  int Offset = 0;
  while(1) {
    Offset = line_buf.Find("\\n", Offset);
    if (Offset == UString::NoMatch) break;
    line_buf.DeleteAt(Offset, 2); // Remove forward slash and new line char 
    line_buf.InsertAt(Offset, "\n");
    Offset++;
  }
  return line_buf.c_str();
}

void BackUp()
{
  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  char buf[CBuf];
  char *FileName;

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  terminal->Write("Backing up the database to another file",
		  p->XPos(), p->YPos());

  terminal->Write("Enter new file name to create: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }

  FileName = tmp;
  POD newdb(FileName, FileName);
  int items = 0;
  CCIndex ccindex(&newdb);
  CCIndex ccindex_old(DB);

  FAU oa;          // Object Address
  VBHeader vb;     // Variable Block Header
  ObjectHeader oh; // Object Header

  FAU vbdfileEOF = DB->OpenDatabase()->GetEOF();
  FAU addr = 0;
  addr = DB->OpenDatabase()->FindFirstVB(addr); // Search the entire file

  if(addr == 0) return; // No variable blocks found in file

  while(1) { 
    if(addr >= vbdfileEOF) break;
    DB->OpenDatabase()->Read(&vb, sizeof(VBHeader), addr);
    if(vb.CkWord == CheckWord) {
      if((__SBYTE__)vb.Status == NormalVB) {
	oa = addr + sizeof(VBHeader);
	DB->OpenDatabase()->Read(&oh, sizeof(ObjectHeader), oa);
        if(oh.ClassID == ccindex.GetClassID()) { 
          ccindex_old.ReadObject(oa);
          ccindex.Copy(ccindex_old);
          ccindex.AddObject(0);
	  items++;
	}
      }
      addr = addr + vb.Length; // Goto the next variable block
    }
    else {
      addr = DB->OpenDatabase()->VBSearch(addr); 
      if(!addr) break;
    }
  }
  
  terminal->Write("Backed up ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(items);
  terminal->Write(" objects to ");
  terminal->Write(tmp);
  terminal->AnyKey(p->XPos(), p->YOffset(2));

  delete[] tmp;
  delete p;
}

void Merge()
{
  if(!AdminRights) {
    terminal->ClearScreen();
    terminal->Write("You do not have Admin User Privileges", 0, 1);
    terminal->AnyKey(0, 2);
    return;
  }

  char buf[CBuf];
  char *FileName;
  
  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  terminal->Write("Merging the contents of another database", 
		  p->XPos(), p->YPos());

  terminal->Write("Enter file name to merge: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);
  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(!VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" does not exists.");
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }

  FileName = tmp;
  POD *newdb = new POD(FileName, FileName, ROMode);

  CCIndex ccindex(newdb);
  CCIndex ccindexa(DB);
  CCIndex ob(DB);
  FAU addr;
  FAU existing;
  
  int x_prev, y_prev, c;
  int imports = 0;
  int updates = 0;
  int updateall = 0;
  
  FAU oa;          // Object Address
  VBHeader vb;     // Variable Block Header
  ObjectHeader oh; // Object Header

  FAU vbdfileEOF = newdb->OpenDatabase()->GetEOF();
  
  addr = 0;
  addr = newdb->OpenDatabase()->FindFirstVB(addr); // Search the entire file

  if(addr == 0) {
    terminal->Write("No variable blocks found in file: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }
  
  terminal->Write("Merging...", p->XPos(), p->YOffset(2));
  
  while(1) { 
    if(addr >= vbdfileEOF) break;
    newdb->OpenDatabase()->Read(&vb, sizeof(VBHeader), addr);
    if(vb.CkWord == CheckWord) {
      if((__SBYTE__)vb.Status == NormalVB) {
	oa = addr + sizeof(VBHeader);
	newdb->OpenDatabase()->Read(&oh, sizeof(ObjectHeader), oa);
        if(oh.ClassID == ccindex.GetClassID()) { 
          ccindex.ReadObject(oa);
          ccindexa.SetCardName(ccindex.GetCardName());
          existing = ccindexa.FindObject();
	  if(existing) {
	    x_prev = p->XPos(); y_prev = p->YPos();
	    if(updateall == 0) {
              terminal->Write(ccindex.GetCardName(), p->XPos(), p->YOffset(2));
	      terminal->Write(" entry already exists."); 
	      terminal->Write("(U)-Update, (A)-Update all, (X)-Exit",
			      p->XPos(), p->YOffset(2));
	      terminal->Write(", Any other key to continue");
	      c = terminal->GetChar();
	    }
	    if(updateall == 1) c = 'a';
	    switch(c) {
	      case 'a': case 'A': 
		updateall = 1;
		updates++;
                ob.Copy(ccindex);
                if(ccindex.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject();
                ob.Copy(ccindex);
		ob.AddObject(0);
		break;
	      case 'u': case 'U': 
		updates++;
                ob.Copy(ccindex);
                if(ccindex.FullCompare(ob)) break; // Object has not changed
		ob.DeleteObject();
                ob.Copy(ccindex);
		ob.AddObject(0);
		break;
	      case 'x': case 'X':
		delete newdb;
		delete p;
		delete[] tmp;
		return;
	      default:
		break;
	    }
	    if(updateall == 1) {
	      p->SetXY(x_prev, y_prev);
	      terminal->ClearLine(x_prev, y_prev+1);
	      terminal->ClearLine(x_prev, y_prev+4);
	      terminal->Write("Updating entry for: ",
			      p->XPos(), p->YOffset(2)); 
              terminal->Write(ccindex.GetCardName(), p->XPos(), p->YOffset(2)); 
	      Sleep((clock_t)1);
	      terminal->ClearLine(p->XPos(), p->YPos());
	      terminal->ClearLine(p->XPos(), p->YPos()-2);
	      p->SetXY(x_prev, y_prev);
	      terminal->MoveCursor(x_prev, y_prev);
	    }
	    else {
	      terminal->ClearLine(p->XPos(), p->YPos());
	      terminal->ClearLine(p->XPos(), p->YPos()-2);
	      p->SetXY(x_prev, y_prev);
	      terminal->MoveCursor(x_prev, y_prev);
	    }
	  }
	  else {
            ccindexa.Copy(ccindex);
            FAU rvaddr = ccindexa.AddObject(0); // Write the object to the file
	    imports++;
	    if(!rvaddr) {
	      x_prev = p->XPos(); y_prev = p->YPos();
	      terminal->Write("Could not add:", p->XPos(), p->YOffset(2));
              terminal->Write(ob.GetCardName());
	      terminal->Write(" to database");
	      terminal->AnyKey(p->XPos(), p->YOffset(2));
	      terminal->ClearLine(p->XPos(), p->YPos());
	      terminal->ClearLine(p->XPos(), p->YPos()-2);
	      p->SetXY(x_prev, y_prev);
	      imports--;
	    }
	  }
	}
      }
      addr = addr + vb.Length; // Goto the next variable block
    }
    else {
      addr = newdb->OpenDatabase()->VBSearch(addr); 
      if(!addr) break;
    }
  }
  
  terminal->Write("Imported ", p->XPos(), p->YOffset(2));
  terminal->Write(imports);
  terminal->Write(" items.");
  if(updates) {
    terminal->Write("Updated ", p->XPos(), p->YOffset(2));
    terminal->Write(updates);
    terminal->Write(" items.");
  }
  terminal->AnyKey(p->XPos(), p->YOffset(2));

  delete newdb;
  delete p;
  delete[] tmp;
}

void CreateTemplate()
{
  char buf[CBuf];
  char *FileName;
  const char dchar = '\t';   // Text delimiter

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  terminal->Write("Creating template file delimited by tabs",
		  p->XPos(), p->YPos());

  terminal->Write("Enter file name to create: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(2));
    if(!yn) {
      delete p;
      delete[] tmp;
      return;
    }
  }

  FileName = tmp;
  delete[] tmp;
  
  ofstream stream(FileName, ios::out); // Open file and truncate it
  terminal->Write("Creating template file: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coords pointer
    return; 
  }

  ASPrint(KeyName, stream, strlen(KeyName));
  stream << dchar;
  ASPrint(M1Name, stream, strlen(M1Name));
  stream << dchar;
  ASPrint(M2Name, stream, strlen(M2Name));
  stream << dchar;
  ASPrint(M3Name, stream, strlen(M3Name));
  stream << dchar;
  ASPrint(M4Name, stream, strlen(M4Name));
  stream << dchar;
  ASPrint(M5Name, stream, strlen(M5Name));
  stream << dchar;
  ASPrint(M6Name, stream, strlen(M6Name));
  stream << dchar;
  ASPrint(M7Name, stream, strlen(M7Name));
  stream << dchar;
  ASPrint(M8Name, stream, strlen(M8Name));
  stream << dchar;
  ASPrint(M9Name, stream, strlen(M9Name));
  stream << dchar;
  ASPrint(Comments, stream, strlen(Comments));
  stream << asLineFeed;

  stream << asLineFeed;
  
  terminal->Write("Finished", p->XPos(), p->YOffset(2));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p;          // Free the Coords pointer
  stream.close();
}

void CompareIndexFile()
{
  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  if(!DB->UsingIndex()) {
    terminal->Write("No index is being used for this database.",
		  p->XPos(), p->YPos());
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;          // Free the Coords pointer
    return;
  }

  terminal->Write("Comparing the index file to the data file...",
		  p->XPos(), p->YPos());

  CCIndex ccindex(DB);

  int rv = ccindex.CompareIndex();
  if(!rv) {
    terminal->Write("The index file does not match the data file!",
		  p->XPos(), p->YOffset(2));
    terminal->Write("The index file needs to be rebuilt.",
		  p->XPos(), p->YOffset(1));
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;          // Free the Coords pointer
    return;
  }

  terminal->Write("The index file checks good.",
		  p->XPos(), p->YOffset(2));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  
  delete p;          // Free the Coords pointer
  return;
}

void RebuildIndexFile()
{
  char buf[CBuf];
  char *FileName;

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);

  if(!DB->UsingIndex()) {
    terminal->Write("No index is being used for this database.",
		  p->XPos(), p->YPos());
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;          // Free the Coords pointer
    return;
  }

  terminal->Write("Rebuilding the index file for this database.",
		  p->XPos(), p->YPos());

  terminal->Write("Enter new file name to create: ",
		  p->XPos(), p->YOffset(2));
  terminal->GetString(buf);

  char *tmp = new char[strlen(buf)+1];
  tmp[strlen(buf)] = '\0';
  strcpy(tmp, buf);
  
  if(VBDFile::Exists(tmp)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }

  terminal->Write("Rebuilding the index file... ",
		  p->XPos(), p->YOffset(2));

  CCIndex ccindex(DB);
  int rv = ccindex.RebuildIndexFile(tmp);
  if(!rv) {
    terminal->Write("The index file was not rebuilt!",
		    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
    return;
  }

    terminal->Write("The index file was rebuilt.",
		    p->XPos(), p->YOffset(2));
    terminal->Write("A new index file named ",
		    p->XPos(), p->YOffset(1));
    terminal->Write(tmp);
    terminal->Write(" was created.");
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p;
    delete[] tmp;
}

void CCIndexDBSearch(CCIndex &ccindex, CCIndexItem item, UString &str,
		     Coords *p, const char *wildcard)
{
  terminal->Write("Searching for ", p->XPos(), p->YOffset(2));
  terminal->Write(str.c_str());

  int offset;
  offset = ObjectsFound = 0;

  if(wildcard != 0) {
    while(1) { // Remove the wild card characters from the string
      offset = str.Find(wildcard, offset);
      if (offset == UString::NoMatch) break;
      str.DeleteAt(offset, strlen(wildcard));
      offset++;
    }
  }

  // Ensure that the memory buffers and the file data
  // stay in sync during multiple file access.
  DB->Index()->TestTree();

  dllist->Clear();
  
  if(wildcard != 0)
    BtreeSearch(DB->Index(), item, ccindex, str, 1);
  else
    BtreeSearch(DB->Index(), item, ccindex, str);

  if(ObjectsFound == 0) {
    terminal->Write("No matches found.", p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(1));
    return;
  }

  if(ObjectsFound > 1) { // Found multiple matches
    terminal->Write("Found ", p->XPos(), p->YOffset(2));
    terminal->Write(ObjectsFound);
    terminal->Write(" matching.");
    terminal->AnyKey(p->XPos(), p->YOffset(1));
  }
  else {
    terminal->Write("Found matching entry for:  ", p->XPos(), p->YOffset(2));
    terminal->Write(str.c_str());
    terminal->AnyKey(p->XPos(), p->YOffset(1));
  }

  // Display all the matches
  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    terminal->ClearScreen();
    p->SetXY(0, 0);
    ccindex.ReadObject(dllistptr->Data.object_address);
    DisplayObject(ccindex, p);
    terminal->Write("(C)-Change, (D)-Delete, (X)-Exit",
		    p->XPos(), p->YOffset(2));
    terminal->Write(", Any other key to continue");
    int c = terminal->GetChar();
    switch(c) {
      case 'c': case 'C': 
	terminal->ClearScreen();
	p->SetXY(0, 0);
        ccindex.ReadObject(dllistptr->Data.object_address);
        ChangeObject(ccindex, p);
	break;
	
      case 'd': case 'D': {
	int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
				 p->XPos(), p->YOffset(2));
	if(!yn) break;
        ccindex.ReadObject(dllistptr->Data.object_address);
        ccindex.DeleteObject();
	break;
      }
      
      case 'x': case 'X':
	dllist->Clear(); // Free dllist memory
	return;

      default:
	break;
    }
    dllistptr = dllistptr->GetNext();
  }
  dllist->Clear(); // Free dllist memory
}

void PostScriptPrint()
{
  Coords *p = new Coords(0, 0);
  terminal->ClearScreen();
  
  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.",
		    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    return;
  }

  char buf[CBuf];
  int i;
  
  // Setup the default values for user configurable PostScript parameters
  int cell_len = 25;    
  double font_size = 10; 
  int orientation = 0;
  int cards_per_page = 2;
  double card_spacing = 1;
  const int DocNameLen = 255;
  char psDocName[DocNameLen];
  for(i = 0; i < DocNameLen; i++) psDocName[i] = 0;
  strcpy(psDocName, ProgramName);
  
  PostScriptDrv::PSPaperSizes paper_code = PostScriptDrv::LETTER_SIZE;
  PostScriptDrv::PSFonts item_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
  PostScriptDrv::PSFonts cell_font = PostScriptDrv::COURIER;

  // Load the PostScript configuration from the config file
  char *CurrentCfgFile;
  char *CfgFile;
  Config *CfgData = new Config;

  // Look for CfgFile name in environment
  if((CurrentCfgFile = getenv(EnvSetting)) == 0)
    CfgFile = (char *)DefaultCfgFile;
  else
    CfgFile = CurrentCfgFile;

  int FileStatus = CfgData->Load((char *)CfgFile);
  if(FileStatus) { // Found a valid config file
// *********************************************************** //
// Add all PostScript config file entries here
// *********************************************************** //
    int int_val = 0;
    double dfp_val = 0;
    char *str_val = 0;

    int_val = CfgData->GetIntValue("CellLength");
    if(int_val > 0) cell_len = int_val;

    dfp_val = CfgData->GetDFPValue("FontSize");
    if(dfp_val > 0) font_size = dfp_val;

    str_val = CfgData->GetStrValue("Orientation");
    if(str_val != 0) {
      if(strcmp(str_val, "PORTRAIT") == 0) orientation = 0;
      str_val = 0;
    }
    
    str_val = CfgData->GetStrValue("PaperSize");
    if(str_val != 0) {
      if(strcmp(str_val, "LETTER") == 0)
	paper_code = PostScriptDrv::LETTER_SIZE;
      if(strcmp(str_val, "LEGAL") == 0)
	paper_code = PostScriptDrv::LEGAL_SIZE;
      if(strcmp(str_val, "TABLOID") == 0)
	paper_code = PostScriptDrv::TABLOID_SIZE;
      if(strcmp(str_val, "A3") == 0) paper_code = PostScriptDrv::A3_SIZE;
      if(strcmp(str_val, "A4") == 0) paper_code = PostScriptDrv::A4_SIZE;
      str_val = 0;
    }

    str_val = CfgData->GetStrValue("ItemBarFont");
    if(str_val != 0) {
      if(strcmp(str_val, "COURIER") == 0)
	item_font = PostScriptDrv::COURIER;
      if(strcmp(str_val, "COURIER_BOLD") == 0)
	item_font = PostScriptDrv::COURIER_BOLD;
      if(strcmp(str_val, "COURIER_OBLIQUE") == 0)
	item_font = PostScriptDrv::COURIER_OBLIQUE;
      if(strcmp(str_val, "COURIER_BOLD_OBLIQUE") == 0)
	item_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
      str_val = 0;
    }
    
    str_val = CfgData->GetStrValue("CellFont");
    if(str_val != 0) {
      if(strcmp(str_val, "COURIER") == 0)
	cell_font = PostScriptDrv::COURIER;
      if(strcmp(str_val, "COURIER_BOLD") == 0)
	cell_font = PostScriptDrv::COURIER_BOLD;
      if(strcmp(str_val, "COURIER_OBLIQUE") == 0)
	cell_font = PostScriptDrv::COURIER_OBLIQUE;
      if(strcmp(str_val, "COURIER_BOLD_OBLIQUE") == 0)
	cell_font = PostScriptDrv::COURIER_BOLD_OBLIQUE;
    }

    int_val = CfgData->GetIntValue("CardsPerPage");
    if(int_val > 0) cards_per_page = int_val;

    dfp_val = CfgData->GetDFPValue("CardSpacing");
    if(dfp_val > 0) card_spacing = dfp_val;

    str_val = CfgData->GetStrValue("psDocumentName");
    if(str_val != 0) {
      for(i = 0; i < DocNameLen; i++) psDocName[i] = 0;
      strcpy(psDocName, str_val);
    }
// *********************************************************** //    
  }
  CfgData->UnLoad(); // Unload the Config file from memory

  for(i = 0; i < CBuf; i++) buf[i] = 0;
  terminal->Write("Enter file name to print to: ",
		  p->XPos(), p->YPos());
  terminal->GetString(buf);

  if(VBDFile::Exists(buf)) {
    terminal->Write("File: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(buf);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(2));
    if(!yn) {
      delete p;
      return;
    }
  }

  ofstream stream(buf, ios::out); // Open file and truncate it
  terminal->Write("Printing to: ",
		  p->XPos(), p->YOffset(2));
  terminal->Write(buf);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(2));
    terminal->Write(buf);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    delete p; // Free the Coords pointer
    return; 
  }

  terminal->Write("Printing...", p->XPos(), p->YOffset(2));

  PostScriptDrv psdrv;
  psdrv.Copies(1);

  // Setup the PostScript driver
  psdrv.SetPaperSize(paper_code);
  psdrv.SetFont(cell_font, font_size);
  psdrv.UseHeader();
  psdrv.DrawHeaderLine();
  if(orientation == 1) psdrv.LandScapeMode();
  psdrv.SetDocumentName(psDocName);
  char time_buf[255]; // Buffer used to hold time/date string
  GetSystemTime(time_buf);
  psdrv.SetDateString(time_buf);
  psdrv.DocumentSetup(); 
  psdrv.Prologue(stream);
  psdrv.MediaSetup(stream);
  CCIndex ccindex(DB);
  int cards = 1;
  int char_offset = int((cell_len + 1) * psdrv.CharWidth());
  psdrv.row = psdrv.StartY();
  
  int x_offset;
  if(orientation == 0)
    x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
  else
    x_offset = int(PRINTABLE_OFFSET_X);
  
  if(DB->RebuildIndex()) {
    terminal->Write("The index file needs to be rebuilt.",
		    p->XPos(), p->YOffset(2));
    terminal->AnyKey(p->XPos(), p->YOffset(1));
    stream.close();
    delete p;
    return;
  }

  LoadIndexKeys();
  psdrv.StartPage(psdrv.page_count + 1, stream);

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    ccindex.ReadObject(dllistptr->Data.object_address);

    PSPrintText(psdrv, stream, (char *)KeyName,  ccindex.GetCardName(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M1Name,  ccindex.GetCompanyName(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M2Name,  ccindex.GetDepartmentName(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M3Name,  ccindex.GetPhoneNumber(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M4Name,  ccindex.GetFaxNumber(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M5Name,  ccindex.GetCellNumber(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M6Name,  ccindex.GetBeeperNumber(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintText(psdrv, stream, (char *)M7Name,  ccindex.GetEmailAddress(),
		char_offset, cell_len, font_size, item_font, cell_font);

    PSPrintMultiText(psdrv, stream, (char *)M8Name,
                     ccindex.GetStreetAddress(),  char_offset, cell_len,
		     font_size, item_font, cell_font);

    PSPrintMultiText(psdrv, stream, (char *)M9Name,
                     ccindex.GetInternetURLS(),  char_offset, cell_len,
		     font_size, item_font, cell_font);

    PSPrintMultiText(psdrv, stream, (char *)Comments,
                     ccindex.GetCommentsBlock(),  char_offset, cell_len,
		     font_size, item_font, cell_font);

    psdrv.row -= int(card_spacing * PIXELS_PER_INCH);
    
    cards++;

    if(cards > cards_per_page) {
      psdrv.EndPage(stream);
      psdrv.StartPage(psdrv.page_count + 1, stream);
      psdrv.row = psdrv.StartY();
      cards = 1;
    }
    dllistptr = dllistptr->GetNext(); 
  } 

  psdrv.EndPage(stream);
  psdrv.Epilogue(stream, psdrv.page_count);

  dllist->Clear(); 
  terminal->Write("Finished", p->XPos(), p->YOffset(2));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  delete p;          // Free the Coords pointer
  stream.close();
}

void PSPrintText(PostScriptDrv &psdrv, ofstream &stream, char *title,
		 char *str, int char_offset, int cell_len, double font_size,
		 PostScriptDrv::PSFonts item_font,
		 PostScriptDrv::PSFonts cell_font)
// Print a single line of a to a PostScript File  
{
  psdrv.ChangeFont(stream, item_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(title, cell_len, stream);
  psdrv.ChangeFont(stream, cell_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
  psdrv.PrintLine(str, stream);
  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
}

void PSPrintMultiText(PostScriptDrv &psdrv, ofstream &stream, char *title,
		      char *str, int char_offset, int cell_len,
		      double font_size, PostScriptDrv::PSFonts item_font,
		      PostScriptDrv::PSFonts cell_font)
// Prints four lines of text to a PostScript file
{
  char words[MAXWORDS][MAXWORDLENGTH];
  int num;
  int offset;
  const char dchar = '\n';  // Text delimiter
  UString line_buf;
  int i, j;

  psdrv.ChangeFont(stream, item_font, font_size);
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
  psdrv.PrintLine(title, cell_len, stream);
  psdrv.ChangeFont(stream, cell_font, font_size);

  for(i = 0; i < MAXWORDS; i++) {
    for(j = 0; j < MAXWORDLENGTH; j++)
      words[i][j] = '\0';
  }
    
  parse(str, words, &num, dchar);
  if(words[0][0] != '\0') {
    line_buf = words[0];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);

  if(words[1][0] != '\0') {
    line_buf = words[1];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
    
  if(words[2][0] != '\0') {
    line_buf = words[2];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }
  
  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);

  if(words[3][0] != '\0') {
    line_buf = words[3];
    offset = line_buf.Find("\r");
    if(offset != UString::NoMatch) {
      line_buf.DeleteAt(offset, 1);
    }
    psdrv.MoveTo(stream, psdrv.StartX() + char_offset, psdrv.row);
    psdrv.PrintLine(line_buf.c_str(), stream);
  }

  psdrv.row -= int(psdrv.FontSize() + psSEPARATOR);
}

// Main prorgram thread
int main(int argc, char **argv)
{
  // Display the program version information and exit the program
  if(argc >= 2) {
    if(strcmp(argv[1], "version") == 0) Version();
  }

  char *FileName;
  char *CurrentCfgFile;
  char *CfgFile;
  Config *CfgData = new Config;
  char *AdminUser = 0;
  
  // Look for CfgFile name in environment
  if((CurrentCfgFile = getenv(EnvSetting)) == 0)
     CfgFile = (char *)DefaultCfgFile;
  else
    CfgFile = CurrentCfgFile;

  // Use default file name if no file is specifed on command line
  if(argc < 2) {
    int FileStatus = CfgData->Load((char *)CfgFile);
    if(!FileStatus) {
       printf("\nCannot locate any database files!\n");
       exit(0);
    }
    else {
      FileName = CfgData->GetStrValue("DBFileName");
// *********************************************************** //
// Add config file entries needed to initialize the program 
// *********************************************************** //
      FileName = CfgData->GetStrValue("DBFileName");
      AdminUser = CfgData->GetStrValue("AdminUser");
// *********************************************************** //
      if(!FileName) {
       printf("\nDBFileName section missing in config file: %s\n", CfgFile);
       exit(0);
      }
    }
  }
  else {
    FileName = argv[1];
  }

  if(!AdminUser) {
    AdminRights = 0;
  }
  else {
    int result = strcmp(AdminUser, "TRUE");
    if(result == 0) {
      AdminRights = 1;
    }
    else {
      AdminRights = 0;
    }
  }

  CfgData->UnLoad(); // Unload the Config file from memory

  // Initialize global database pointer
  // NOTE: Index file and data file will share the same file name
  // with different file extensions. The data file will have a 
  // .pod extension and the index file will have a .btx extension.
  if(AdminRights == 1) 
    DB = new POD(FileName, RWMode, 1, CacheSize);
  else
    DB = new POD(FileName, ROMode, 1, CacheSize);

  terminal->init();
  MainMenu();
  terminal->finish();
  delete DB;
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //
