// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: strdbt.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/29/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.

- Modify "dbconfig.h" file to customize the database
- Modify "dbvers.h" file to uniquely identify the database
- Modify "dbtcfg.h" file to customize this program
- Modify "version.h" file to report changes and fixes
*/
// ----------------------------------------------------------- // 
#include <fstream.h> // Using ofstream to print to a text file
#include "strdb_sh.h"
#include "terminal.h"
#include "config.h"
#include "timer.h"
#include "strutil.h"
#include "asprint.h"
#include "pscript.h"
#include "vbdstats.h"
#include "btwalker.h"
#include "htmldrv.h"

// Application specific include files
#include "version.h"
#include "dbconfig.h"
#include "dbtcfg.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 = 15;   // 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 = 10; // Find menu selections

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

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

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

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

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

DMF DMFunctions[DMSelections] = {
  &Return,
  &DisplayLBL,
  &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 = 6; // Print menu selections

// Print menu functions
void ASCIIPrintAll();     // Print database to an ASCII file (abbreviated)
void ASCIIPrintAllLong(); // Print database to an ASCII file (full listing) 
void ASCIIPrintObject();  // Print a single object to an ASCII file
void PostScriptPrint();   // Print database to a PostScript file
void HTMLPrint();         // Print the database to an html file

const char *printmenu[PMSelections] = {
  "(0) - Return to main menu",
  "(1) - Print to text file (abbreviated, 80 cols)",
  "(2) - Print to text file (full listing, 132 cols)",
  "(3) - Print a single object to a text file",
  "(4) - Print to PostScript file",
  "(5) - Print to an HTML file"
};

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

// Function prototypes for functions use by all menu functions
void AddObject(StrDB &strdb, Coords *p);
void ChangeObject(StrDB &strdb, Coords *p, int adding = 0);
void DisplayObject(StrDB &strdb, Coords *p);
void LineByLine(StrDB &strdb, Coords *p);
void DisplayString(const char *s);
int LoadIndexKeys();

// Search functions
void FindBy(const char *MemberName, StrDBItem item);
void StrDBSearch(StrDB &strdb, StrDBItem item, UString &str,
		 Coords *p, const char *wildcard = 0);

enum PrintMode {
  PORTRAIT,
  LANDSCAPE
};

void PrintItemBar(ofstream &stream, int abv = 1);
void PrintLineByLine(StrDB &strdb, ofstream &stream, int abv = 1);
int ASCIIPrint(PrintMode mode);
void PrintObject(StrDB &strdb, Coords *p);
int PrintPSItemBar(ofstream &stream, PostScriptDrv &psdrv,
		    int x_offset, int char_offset, int max_len);

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(M2Name != 0) {
      terminal->Write("(2) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M2Name);
    }
    if(M3Name != 0) {
      terminal->Write("(3) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M3Name);
    }
    if(M4Name != 0) {
      terminal->Write("(4) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M4Name);
    }
    if(M5Name != 0) {
      terminal->Write("(5) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M5Name);
    }
    if(M6Name != 0) {
      terminal->Write("(6) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M6Name);
    }
    if(M7Name != 0) {
      terminal->Write("(7) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M7Name);
    }
    if(M8Name != 0) {
      terminal->Write("(8) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(M8Name);
    }
    if(Comments != 0) {
      terminal->Write("(9) - Find by ", p->XPos(), p->YOffset(1));
      terminal->Write(Comments);
    }
    
    terminal->Write("Press the number 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;
      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;

      case '2':
	(*DMFunctions[2]) (); // 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;
      case '4':
	(*PMFunctions[4]) (); // Call the appropriate menu function
	break;
      case '5':
	(*PMFunctions[5]) (); // 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;
  }

  StrDB strdb(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
    strdb.ReadObject(dllistptr->Data.object_address);
    DisplayObject(strdb, p);
    terminal->Write("(C)-Change, (D)-Delete, (P)-Print, (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(strdb, 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;
	strdb.DeleteObject();
	break;
      }
      
      case 'p': case 'P':
	p->YOffset(1);
	PrintObject(strdb, p);
	break;
	
      case 'x': case 'X':
	dllist->Clear();
	delete p;
	return;
	      
      default:
	break;
    }
    dllistptr = dllistptr->GetNext(); 
  }

  dllist->Clear();
  delete p;
}

void ItemBar(Coords *p)
// Displays the Key data member and the data member 2 - 5
{
  int x = p->XPos(); 
  if(KeyName != 0) terminal->Write(KeyName, p->XPos(), p->YPos());
  if(M2Name != 0)
    terminal->Write(M2Name, p->XOffset(StringOffset+1), p->YPos());
  if(M3Name != 0)
    terminal->Write(M3Name, p->XOffset(StringOffset+1), p->YPos());
  if(M4Name != 0)
    terminal->Write(M4Name, p->XOffset(StringOffset+1), p->YPos());
  if(M5Name != 0)
    terminal->Write(M5Name, p->XOffset(StringOffset+1), p->YPos());
  terminal->MoveCursor(0, p->YOffset(1));
  int i = 0;
  while (i++ < terminal->MaxCols()-1) terminal->Write('=');
  p->YOffset(1);
  p->SetX(x);
}

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

  StrDB strdb(DB);
  int c;
  unsigned count = 0;
  Coords *p = new Coords(0, 0);
  terminal->ClearScreen();

  ItemBar(p);

  LoadIndexKeys();

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    // Display the first object
    strdb.ReadObject(dllistptr->Data.object_address);
    LineByLine(strdb, p);
    p->YOffset(1);
    dllistptr = dllistptr->GetNext(); 
    count++;
    if(count > 5) {
      terminal->Write("(X)-Exit", p->XPos(), p->YOffset(2));
      terminal->Write(", Any other key to continue");
      c = terminal->GetChar();
      switch(c) {
	case 'x': case 'X':
	  dllist->Clear();
	  delete p; // Free Coords pointer
	  return;
		
	default:
	  break;
      }
      terminal->ClearScreen();
      p->SetXY(0, 0);
      ItemBar(p);
      count = 0;
    }
  }

  terminal->AnyKey(p->XPos(), p->YOffset(2));
  dllist->Clear();
  delete p; // Free Coords pointer
}

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);

  StrDB strdb(DB);
  strdb.SetKM(buf);

  FAU addr = strdb.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;
  }
  
  strdb.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 PrintLineByLine(StrDB &strdb, ofstream &stream, int abv)
{
  int i = 0;
  
  if(KeyName != 0) ASPrint(strdb.GetKM(), stream, StringOffset+1);
  if(M2Name != 0) ASPrint(strdb.GetM2(), stream, StringOffset+1);
  if(M3Name != 0) ASPrint(strdb.GetM3(), stream, StringOffset+1);
  if(M4Name != 0) ASPrint(strdb.GetM4(), stream, StringOffset+1);
  if(abv) { // Print abbreviated list for portrait print outs
    if(M5Name != 0) ASPrint(strdb.GetM5(), 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(strdb.GetM5(), stream, StringOffset+1);
    if(M6Name != 0) ASPrint(strdb.GetM6(), stream, StringOffset+1);
    if(M7Name != 0) ASPrint(strdb.GetM7(), stream, StringOffset+1);
    if(M8Name != 0) ASPrint(strdb.GetM8(), stream, StringOffset);
    stream << asLineFeed;
    i = 0;
    while (i++ < asPrintColsLong-1) stream << '-';
    stream << asLineFeed;
  }
}

void ASCIIPrintAll()
{
  ASCIIPrint(PORTRAIT);
}

void ASCIIPrintAllLong()
{
  ASCIIPrint(LANDSCAPE);
}

int ASCIIPrint(PrintMode mode)
{
  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 0;
    }
  }

  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 0; 
  }

  terminal->Write("Printing...", p->XPos(), p->YOffset(2));
		  
  StrDB strdb(DB);
  unsigned count = 0;

  if(mode == LANDSCAPE)
    PrintItemBar(stream, 0);
  else
    PrintItemBar(stream);

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

  LoadIndexKeys();

  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    strdb.ReadObject(dllistptr->Data.object_address);
    switch(mode) {
      case PORTRAIT: 
	PrintLineByLine(strdb, stream);
	break;
      case LANDSCAPE: 
	PrintLineByLine(strdb, stream, 0);	      
	break;
      default:
        delete p; // Free the Coords pointer
	return 0;
    }
    count++; 
    dllistptr = dllistptr->GetNext(); 
  }

  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();
  return count;
}

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

  StrDB strdb(DB);
  strdb.SetKM(buf);

  FAU addr = strdb.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;
  }

  PrintObject(strdb, p);
  delete p; // Free Coords pointer
}

void PrintObject(StrDB &strdb, Coords *p)
{
  char buf[CBuf];
  char *FileName;
  const char *separator = " = ";
  const char *cseparator = ": ";
  
  terminal->Write("Enter file name to print to: ",
		  p->XPos(), p->YOffset(1));
  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(1));
    terminal->Write(tmp);
    terminal->Write(" already exists.");
    int yn = terminal->YesNo("Overwrite it (y/n)?", p->XPos(), p->YOffset(1));
    if(!yn) {
      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(1));
  terminal->Write(FileName);
  
  if(!stream) { // Could not open the stream
    terminal->Write("Could not write to: ",
		    p->XPos(), p->YOffset(1));
    terminal->Write(FileName);
    terminal->AnyKey(p->XPos(), p->YOffset(2));
    return;
  }

  if(KeyName != 0) {
    ASPrint(KeyName, stream, strlen(KeyName));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetKM(), stream, strlen(strdb.GetKM()));
    stream << asLineFeed;
  }
  if(M2Name != 0) {
    ASPrint(M2Name, stream, strlen(M2Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM2(), stream, strlen(strdb.GetM2()));
    stream << asLineFeed;
  }
  if(M3Name != 0) {
    ASPrint(M3Name, stream, strlen(M3Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM3(), stream, strlen(strdb.GetM3()));
    stream << asLineFeed;
  }
  if(M4Name != 0) {
    ASPrint(M4Name, stream, strlen(M4Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM4(), stream, strlen(strdb.GetM4()));
    stream << asLineFeed;
  }
  if(M5Name != 0) {
    ASPrint(M5Name, stream, strlen(M5Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM5(), stream, strlen(strdb.GetM5()));
    stream << asLineFeed;
  }
  if(M6Name != 0) {
    ASPrint(M6Name, stream, strlen(M6Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM6(), stream, strlen(strdb.GetM6()));
    stream << asLineFeed;
  }
  if(M7Name != 0) {
    ASPrint(M7Name, stream, strlen(M7Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM7(), stream, strlen(strdb.GetM7()));
    stream << asLineFeed;
  }
  if(M8Name != 0) {
    ASPrint(M8Name, stream, strlen(M8Name));
    ASPrint(separator, stream, strlen(separator));
    ASPrint(strdb.GetM8(), stream, strlen(strdb.GetM8()));
    stream << asLineFeed;
  }
  if(Comments != 0) {
    ASPrint(Comments, stream, strlen(Comments));
    ASPrint(cseparator, stream, strlen(cseparator));
    ASPrint(strdb.GetCM(), stream, strlen(strdb.GetCM()));
    stream << asLineFeed;
  }
  terminal->Write("Finished", p->XPos(), p->YOffset(1));
  terminal->AnyKey(p->XPos(), p->YOffset(2));
  stream.close();
}

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

  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;
    }
  }
  
  StrDB strdb(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;
  }

  strdb.SetKM(key_buf);

  FAU addr = strdb.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(strdb, p);
  delete p; // Free Coords pointer
}

void AddObject(StrDB &strdb, Coords *p)
{
  char buf[KeyBuf];
  char cbuf[CBuf];

  if(M2Name != 0) {
  terminal->Write(M2Name, p->XPos(), p->YOffset(2));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  strdb.SetM2(buf);
  }
  if(M3Name != 0) {
  terminal->Write(M3Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  strdb.SetM3(buf);
  }
  if(M4Name != 0) {
  terminal->Write(M4Name, p->XPos(), p->YOffset(1));
  terminal->Write(" -> ");
  terminal->GetString(buf);
  strdb.SetM4(buf);
  }
  if(M5Name != 0) {
    terminal->Write(M5Name, p->XPos(), p->YOffset(1));
    terminal->Write(" -> ");
    terminal->GetString(buf);
    strdb.SetM5(buf);
  }
  if(M6Name != 0) {
    terminal->Write(M6Name, p->XPos(), p->YOffset(1));
    terminal->Write(" -> ");
    terminal->GetString(buf);
    strdb.SetM6(buf);
  }
  if(M7Name != 0) {
    terminal->Write(M7Name, p->XPos(), p->YOffset(1));
    terminal->Write(" -> ");
    terminal->GetString(buf);
    strdb.SetM7(buf);
  }
  if(M8Name != 0) {
    terminal->Write(M8Name, p->XPos(), p->YOffset(1));
    terminal->Write(" -> ");
    terminal->GetString(buf);
    strdb.SetM8(buf);
  }
  if(Comments != 0) {
    terminal->Write(Comments, p->XPos(), p->YOffset(1));
    terminal->Write(" -> ");
    terminal->GetString(cbuf);
    strdb.SetCM(cbuf);
  }
  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(strdb, p, 1); 
      break;
    case 'a': case 'A':
      return;
    default:
      break;
  }

  FAU addr = strdb.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; 
}

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

  terminal->ClearScreen();
  Coords *p = new Coords(0, 0);
  char buf[KeyBuf];
  unsigned offset = 0;
  
  StrDB strdb(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
    strdb.SetKM(buf);
    StrDBSearch(strdb, item, str, p);
    delete p; // Free the Coord pointer
    return;
  }

  StrDBSearch(strdb, item, str, p, WildCard);
  delete p; // Free the Coord pointer
}

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

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

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

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

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

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

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

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

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

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);

  StrDB strdb(DB);
  strdb.SetKM(buf);

  FAU addr = strdb.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(strdb, p);
  delete p; // Free Coords pointer
}

void ChangeObject(StrDB &strdb, Coords *p, int adding)
{
  char buf[KeyBuf];
  char cbuf[CBuf];
  
  // Buffer the object's data to allow changes to be canceled
  StrDB ob;
  ob.Copy(strdb);
  FAU addr;
  StrDB 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) {
    p->SetXY(x_prev, y_prev);
    terminal->Write("(C) Cancel change", p->XPos(), p->YOffset(2));
    if(adding) {
      if(KeyName != 0) {
	terminal->Write("(0) ", p->XPos(), p->YOffset(1));
	terminal->Write(KeyName); terminal->Write(" = ");
	terminal->Write(strdb.GetKM());
	terminal->ClearLine();
      }
    }
    else {
      if(KeyName != 0) {
	terminal->Write("(-) ", p->XPos(), p->YOffset(1));
	terminal->Write(KeyName); terminal->Write(" = ");
	terminal->Write(strdb.GetKM());
	terminal->ClearLine();
      }
    }
    if(M2Name != 0) {
      terminal->Write("(1) ", p->XPos(), p->YOffset(1));
      terminal->Write(M2Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM2());
      terminal->ClearLine();
    }
    if(M3Name != 0) {
      terminal->Write("(2) ", p->XPos(), p->YOffset(1));
      terminal->Write(M3Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM3());
      terminal->ClearLine();
    }
    if(M4Name != 0) {
      terminal->Write("(3) ", p->XPos(), p->YOffset(1));
      terminal->Write(M4Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM4());
      terminal->ClearLine();
    }
    if(M5Name != 0) {
      terminal->Write("(4) ", p->XPos(), p->YOffset(1));
      terminal->Write(M5Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM5());
      terminal->ClearLine();
    }
    if(M6Name != 0) {
      terminal->Write("(5) ", p->XPos(), p->YOffset(1));
      terminal->Write(M6Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM6());
      terminal->ClearLine();
    }
    if(M7Name != 0) {
      terminal->Write("(6) ", p->XPos(), p->YOffset(1));
      terminal->Write(M7Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM7());
      terminal->ClearLine();
    }
    if(M8Name != 0) {
      terminal->Write("(7) ", p->XPos(), p->YOffset(1));
      terminal->Write(M8Name); terminal->Write(" = ");
      terminal->Write(strdb.GetM8());
      terminal->ClearLine();
    }
    if(Comments != 0) {
      terminal->Write("(8) ", p->XPos(), p->YOffset(1));
      terminal->Write(Comments); terminal->Write(" = ");
      terminal->Write(strdb.GetCM());
      terminal->ClearLine();
    }
    terminal->Write("(A) Accept changes", p->XPos(), p->YOffset(1));
    
    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': 
        strdb.Copy(ob);
	return;

      case '0':
	// User not allowed to change name unless adding a new object
	if(!adding) break; 
	if(KeyName != 0) { 
	  terminal->Write(KeyName, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetKM(buf);
	  addr = strdb.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;
	  }
	  strdb.SetKM(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '1':
	if(M2Name != 0) { 
	  terminal->Write(M2Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM2(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '2':
	if(M3Name != 0) { 
	  terminal->Write(M3Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM3(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;
	
      case '3':
	if(M4Name != 0) { 
	  terminal->Write(M4Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM4(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '4':
	if(M5Name != 0) { 
	  terminal->Write(M5Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM5(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '5':
	if(M6Name != 0) { 
	  terminal->Write(M6Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM6(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '6':
	if(M7Name != 0) { 
	  terminal->Write(M7Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM7(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '7':
	if(M8Name != 0) { 
	  terminal->Write(M8Name, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(buf);
	  strdb.SetM8(buf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;

      case '8':
	if(Comments != 0) { 
	  terminal->Write(Comments, x, y);
	  terminal->Write(" -> ");
	  terminal->GetString(cbuf);
	  strdb.SetCM(cbuf);
	  terminal->ClearLine(x, y);
	  terminal->MoveCursor(x_prev, y_prev);
	}
	break;
	
      case 'A': case 'a':
	if(adding) return; // New object, no need to reallocate space
        if(strdb.FullCompare(ob)) return; // The object has not changed
        changed.Copy(strdb);  // Make a copy of the changes

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

void DisplayObject(StrDB &strdb, Coords *p)
// Display's the complete object with all details.
{
  if(KeyName != 0) {
    terminal->Write(KeyName, p->XPos(), p->YPos());
    terminal->Write(" = ");
    terminal->Write(strdb.GetKM());
  }
  if(M2Name != 0) {
    terminal->Write(M2Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM2());
  }
  if(M3Name != 0) {
    terminal->Write(M3Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM3());
  }
  if(M4Name != 0) {
    terminal->Write(M4Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM4());
  }
  if(M5Name != 0) { 
    terminal->Write(M5Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM5());
  }
  if(M6Name != 0) {
    terminal->Write(M6Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM6());
  }
  if(M7Name != 0) {
    terminal->Write(M7Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM7());
  }
  if(M8Name != 0) {
    terminal->Write(M8Name, p->XPos(), p->YOffset(1));
    terminal->Write(" = ");
    terminal->Write(strdb.GetM8());
  }
  if(Comments != 0) {
    terminal->Write(Comments, p->XPos(), p->YOffset(1));
    terminal->Write(": ");
    terminal->Write(strdb.GetCM());
  }
}

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 LineByLine(StrDB &strdb, Coords *p)
// Display objects line by line with partial details
{
  int x = p->XPos();

  if(KeyName != 0) {
    terminal->MoveCursor(p->XPos(), p->YPos());
    DisplayString(strdb.GetKM());
  }

  if(M2Name != 0) {
    terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos()); 
    DisplayString(strdb.GetM2());
  }
  if(M3Name != 0) {
    terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos());
    DisplayString(strdb.GetM3());
  }
  if(M4Name != 0) {
    terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos());
    DisplayString(strdb.GetM4());
  }
  if(M5Name != 0) {
    terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos());
    DisplayString(strdb.GetM5());
  }

  int i = 0;
  terminal->MoveCursor(0, p->YOffset(1));
  while (i++ < terminal->MaxCols()-1) terminal->Write('-');
  p->SetX(x);
}

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 tabs
  const char *Fill = "None"; // 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; 
  }

  StrDB strdb(DB);

  // Write the item bar to the file first
  if(KeyName != 0) {
    ASPrint(KeyName, stream, strlen(KeyName));
    stream << dchar;
  }
  if(M2Name != 0) {
    ASPrint(M2Name, stream, strlen(M2Name));
    stream << dchar;
  }
  if(M3Name != 0) {
    ASPrint(M3Name, stream, strlen(M3Name));
    stream << dchar;
  }
  if(M4Name != 0) {
    ASPrint(M4Name, stream, strlen(M4Name));
    stream << dchar;
  }
  if(M5Name != 0) {
    ASPrint(M5Name, stream, strlen(M5Name));
    stream << dchar;
  }
  if(M6Name != 0) {
    ASPrint(M6Name, stream, strlen(M6Name));
    stream << dchar;
  }
  if(M7Name != 0) {
    ASPrint(M7Name, stream, strlen(M7Name));
    stream << dchar;
  }
  if(M8Name != 0) {
    ASPrint(M8Name, stream, strlen(M8Name));
    stream << dchar;
  }
  if(Comments != 0) {
    ASPrint(Comments, stream, strlen(Comments));
  }
  stream << asLineFeed;
  
  int exports = 0;

  LoadIndexKeys();
  
  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    strdb.ReadObject(dllistptr->Data.object_address);
    if(KeyName != 0) {
      ASPrint(strdb.GetKM(), filter, stream, strlen(strdb.GetKM()));
      stream << dchar;
    }
    if(M2Name != 0) {
      if(!*strdb.GetM2()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM2(), filter, stream, strlen(strdb.GetM2()));
      stream << dchar;
    }
    if(M3Name != 0) {
      if(!*strdb.GetM3()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM3(), filter, stream, strlen(strdb.GetM3()));
      stream << dchar;
    }
    if(M4Name != 0) {
      if(!*strdb.GetM4()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM4(), filter, stream, strlen(strdb.GetM4()));
      stream << dchar;
    }
    if(M5Name != 0) {
      if(!*strdb.GetM5()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM5(), filter, stream, strlen(strdb.GetM5()));
      stream << dchar;
    }
    if(M6Name != 0) {
      if(!*strdb.GetM6()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM6(), filter, stream, strlen(strdb.GetM6()));
      stream << dchar;
    }
    if(M7Name != 0) {
      if(!*strdb.GetM7()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM7(), filter, stream, strlen(strdb.GetM7()));
      stream << dchar;
    }
    if(M8Name != 0) {
      if(!*strdb.GetM8()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetM8(), filter, stream, strlen(strdb.GetM8()));
      stream << dchar;
    }
    if(Comments != 0) {
      if(!*strdb.GetCM()) ASPrint(Fill, stream, strlen(Fill));
      else ASPrint(strdb.GetCM(), filter, stream, strlen(strdb.GetCM()));
    }
    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 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;
  
  StrDB strdb(DB);
  StrDB strdba(DB);
  StrDB ob(DB);
  
  int linecount = 0;
  int imports = 0;
  int updates = 0;
  int updateall = 0;
  int ObjectItems = 1; // Number of class data members 
  int x_prev, y_prev, c;
  FAU addr;
  
  // Calculate the number of object items
  if(M2Name != 0) ObjectItems++;
  if(M3Name != 0) ObjectItems++;
  if(M4Name != 0) ObjectItems++;
  if(M5Name != 0) ObjectItems++;
  if(M6Name != 0) ObjectItems++;
  if(M7Name != 0) ObjectItems++;
  if(M8Name != 0) ObjectItems++;
  if(Comments != 0) ObjectItems++;

  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) {
      strdb.SetKM(words[0]);
      if(M2Name != 0) strdb.SetM2(words[1]);
      if(M3Name != 0) strdb.SetM3(words[2]);
      if(M4Name != 0) strdb.SetM4(words[3]);
      if(M5Name != 0) strdb.SetM5(words[4]);
      if(M6Name != 0) strdb.SetM6(words[5]);
      if(M7Name != 0) strdb.SetM7(words[6]);
      if(M8Name != 0) strdb.SetM8(words[7]);
      if(Comments != 0) strdb.SetCM(words[8]);
      strdba.SetKM(strdb.GetKM());
      addr = strdba.FindObject();
      if(addr) { 
	x_prev = p->XPos(); y_prev = p->YPos();
	if(updateall == 0) {
	  terminal->Write(strdb.GetKM(), 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(strdba);
            if(strdb.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject(); 
            strdb.AddObject(0);
	    break;
	  case 'u': case 'U': 
	    updates++;
	    ob.Copy(strdba);
            if(strdb.FullCompare(ob)) break; // The object has not changed
	    ob.DeleteObject();
            strdb.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(strdb.GetKM(), 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 = strdb.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(strdb.GetKM());
	    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();
}

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;
  StrDB strdb(&newdb);
  StrDB strdb_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 == strdb.GetClassID()) { 
	  strdb_old.ReadObject(oa);
	  strdb.Copy(strdb_old);
	  strdb.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);

  StrDB strdb(newdb);
  StrDB strdba(DB);
  StrDB 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 == strdb.GetClassID()) { 
	  strdb.ReadObject(oa);
	  strdba.SetKM(strdb.GetKM());
	  existing = strdba.FindObject();
	  if(existing) {
	    x_prev = p->XPos(); y_prev = p->YPos();
	    if(updateall == 0) {
	      terminal->Write(strdb.GetKM(), 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(strdb);
		if(strdb.FullCompare(ob)) break; // The object has not changed
		ob.DeleteObject();
		ob.Copy(strdb);
		ob.AddObject(0);
		break;
	      case 'u': case 'U': 
		updates++;
		ob.Copy(strdb);
		if(strdb.FullCompare(ob)) break; // The object has not changed
		ob.DeleteObject();
		ob.Copy(strdb);
		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(strdb.GetKM(), 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 {
	    strdba.Copy(strdb);
	    FAU rvaddr = strdba.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.GetKM());
	      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; 
  }

  if(KeyName != 0) {
    ASPrint(KeyName, stream, strlen(KeyName));
    stream << dchar;
  }
  if(M2Name != 0) {
    ASPrint(M2Name, stream, strlen(M2Name));
    stream << dchar;
  }
  if(M3Name != 0) {
    ASPrint(M3Name, stream, strlen(M3Name));
    stream << dchar;
  }
  if(M4Name != 0) {
    ASPrint(M4Name, stream, strlen(M4Name));
    stream << dchar;
  }
  if(M5Name != 0) {
    ASPrint(M5Name, stream, strlen(M5Name));
    stream << dchar;
  }
  if(M6Name != 0) {
    ASPrint(M6Name, stream, strlen(M6Name));
    stream << dchar;
  }
  if(M7Name != 0) {
    ASPrint(M7Name, stream, strlen(M7Name));
    stream << dchar;
  }
  if(M8Name != 0) {
    ASPrint(M8Name, stream, strlen(M8Name));
    stream << dchar;
  }
  if(Comments != 0) {
    ASPrint(Comments, stream, strlen(Comments));
  }
  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());

  StrDB strdb(DB);

  int rv = strdb.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));

  StrDB strdb(DB);
  int rv = strdb.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 StrDBSearch(StrDB &strdb, StrDBItem 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, strdb, str, 1);
  else
    BtreeSearch(DB->Index(), item, strdb, 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);
    strdb.ReadObject(dllistptr->Data.object_address);
    DisplayObject(strdb, p);
    terminal->Write("(C)-Change, (D)-Delete, (P)-Print, (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);
	strdb.ReadObject(dllistptr->Data.object_address);
        ChangeObject(strdb, 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;
	strdb.ReadObject(dllistptr->Data.object_address);
        strdb.DeleteObject();
	break;
      }
      
      case 'p': case 'P':
	strdb.ReadObject(dllistptr->Data.object_address);
	p->YOffset(1);
        PrintObject(strdb, p);
	break;

      case 'x': case 'X':
	dllist->Clear(); // Free dllist memory
	return;

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

int PrintPSItemBar(ofstream &stream, PostScriptDrv &psdrv,
		    int x_offset, int char_offset, int cell_len)
{
  if(KeyName != 0) {
    psdrv.PrintLine((char *)KeyName, cell_len, stream);
    x_offset += char_offset;
  } 
  if(M2Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M2Name, cell_len, stream);
    x_offset += char_offset;
  }
  
  if(M3Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M3Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(M4Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M4Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(M5Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M5Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(M6Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M6Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(M7Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M7Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(M8Name != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)M8Name, cell_len, stream);
    x_offset += char_offset;
  }

  if(Comments != 0) {
    psdrv.MoveTo(stream, x_offset, psdrv.row);
    psdrv.PrintLine((char *)Comments, cell_len, stream);
    x_offset += char_offset;
  }
  return x_offset;
}

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 = 19;    
  double font_size = 7; 
  int orientation = 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;
    }

    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();
  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);
  StrDB strdb(DB);
  int char_offset = int((cell_len + 1) * psdrv.CharWidth());
  int x_offset;
  if(orientation == 0)
    x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
  else
    x_offset = PRINTABLE_OFFSET_X;

  int lines = 4;
  psdrv.StartPage(psdrv.page_count + 1, stream);
  psdrv.row = psdrv.StartY();
  psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);

  psdrv.ChangeFont(stream, item_font, font_size);
  x_offset = PrintPSItemBar(stream, psdrv, x_offset, char_offset, cell_len);
  psdrv.ChangeFont(stream, cell_font, font_size);
  
  int line_points;
  if(orientation == 0)
    line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
  else
    line_points = x_offset - PRINTABLE_OFFSET_X;

  psdrv.row -= (int)psdrv.FontSize();
  lines++;
  psdrv.drawThickLine(stream, line_points, psdrv.StartX(), psdrv.row);
  psdrv.row -= THICK_LINE_WIDTH + psdrv.FontSize();
  lines++;

  LoadIndexKeys();
  
  dllistptr = dllist->GetFront();
  while(!dllist->IsHeader(dllistptr)) {
    strdb.ReadObject(dllistptr->Data.object_address);
    if(orientation == 0)
      x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
    else
      x_offset = PRINTABLE_OFFSET_X;

    if(KeyName != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetKM(), cell_len, stream);
      x_offset += char_offset;
    } 
    if(M2Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM2(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(M3Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM3(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(M4Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM4(), cell_len, stream);
	x_offset += char_offset;
    }
    
    if(M5Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM5(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(M6Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM6(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(M7Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM7(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(M8Name != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetM8(), cell_len, stream);
      x_offset += char_offset;
    }
    
    if(Comments != 0) {
      psdrv.MoveTo(stream, x_offset, psdrv.row);
      psdrv.PrintLine(strdb.GetCM(), cell_len, stream);
      x_offset += char_offset;
    }
    psdrv.row -= psdrv.FontSize();
    lines++;
    if(orientation == 0)
      line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
    else
      line_points = x_offset - PRINTABLE_OFFSET_X;
    psdrv.drawLine(stream, line_points, psdrv.StartX(), psdrv.row);
    psdrv.row -= LINE_WIDTH + psdrv.FontSize();
    lines++; // Increment the line count

    if(lines >= psdrv.LinesPerPage()) {
      psdrv.EndPage(stream);
      psdrv.StartPage(psdrv.page_count + 1, stream);
      if(orientation == 0)
	x_offset = int(HEADER_OFFSET * PIXELS_PER_INCH);
      else
	x_offset = PRINTABLE_OFFSET_X;
      psdrv.row = psdrv.StartY();
      lines = 4;
      psdrv.MoveTo(stream, psdrv.StartX(), psdrv.row);
      psdrv.ChangeFont(stream, item_font, font_size);
      x_offset = PrintPSItemBar(stream, psdrv, x_offset,
				char_offset, cell_len);
      psdrv.ChangeFont(stream, cell_font, font_size);
      if(orientation == 0)
	line_points = int(x_offset - (HEADER_OFFSET * PIXELS_PER_INCH));
      else
	line_points = x_offset - PRINTABLE_OFFSET_X;
      psdrv.row -= (int)psdrv.FontSize();
      lines++;
      psdrv.drawThickLine(stream, line_points, psdrv.StartX(), psdrv.row);
      psdrv.row -= THICK_LINE_WIDTH + psdrv.FontSize();
      lines ++;
    }
    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 HTMLPrint()
{
  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;
  
  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));
  LoadIndexKeys();

  HyperText htm(stream);
  char date[255]; htm.GetSystemTime(date);
  htm << comment << "HTML file generated by: " << ProgramName << " version ";
  htm.precision(3);
  htm << VersionNumber << ecomment << endl;
  htm << comment << "File Creation date: " << date << ecomment << endl;

  htm.Prologue(ProgramName);
  htm.StartBody("BGCOLOR=\"#FFFFFF\"");
  htm.eat_space(); // Use non-breaking spaces
  htm.GenTable();
  int num_columns = 0;

  // Calculate the number of columns
  if(KeyName != 0) num_columns++; 
  if(M2Name != 0) num_columns++; 
  if(M3Name != 0) num_columns++; 
  if(M4Name != 0) num_columns++; 
  if(M5Name != 0) num_columns++; 
  if(M6Name != 0) num_columns++; 
  if(M7Name != 0) num_columns++; 
  if(M8Name != 0) num_columns++; 
  if(Comments != 0) num_columns++;

  // Write the table title
  htm.StartTableRow();
  htm.TableHeader("CENTER", num_columns, 1, 10);
  char sys_time[255];
  htm.GetSystemTime(sys_time);
  htm << ' ' << ProgramName << ' ' << br << ' ' << sys_time << ' ';
  htm.EndTableHeader();
  htm.EndTableRow();
  
  // Write the table headers
  htm.StartTableRow();
  if(KeyName != 0) {
    htm.TableHeader();
    htm << ' ' << KeyName << ' ';
    htm.EndTableHeader();
  } 
  if(M2Name != 0) {
    htm.TableHeader();
    htm << ' ' << M2Name << ' ';
    htm.EndTableHeader();
  }
  if(M3Name != 0) {
    htm.TableHeader();
    htm << ' ' << M3Name << ' ';
    htm.EndTableHeader();
  }
  if(M4Name != 0) {
    htm.TableHeader();
    htm << ' ' << M4Name << ' ';
    htm.EndTableHeader();
  }
  if(M5Name != 0) {
    htm.TableHeader();
    htm << ' ' << M5Name << ' ';
    htm.EndTableHeader();
  }
  if(M6Name != 0) {
    htm.TableHeader();
    htm << ' ' << M6Name << ' ';
    htm.EndTableHeader();
  }
  if(M7Name != 0) {
    htm.TableHeader();
    htm << ' ' << M7Name << ' ';
    htm.EndTableHeader();
  }
  if(M8Name != 0) {
    htm.TableHeader();
    htm << ' ' << M8Name << ' ';
    htm.EndTableHeader();
  }
  if(Comments != 0) {
    htm.TableHeader();
    htm << ' ' << Comments << ' ';
    htm.EndTableHeader();
  }
  htm.EndTableRow();
  
  StrDB strdb(DB);
  dllistptr = dllist->GetFront();

  while(!dllist->IsHeader(dllistptr)) {
    strdb.ReadObject(dllistptr->Data.object_address);
    htm.StartTableRow();
    if(KeyName != 0) {
      htm.TableData();
      htm << strdb.GetKM();
      htm.EndTableData();
    } 
    if(M2Name != 0) {
      htm.TableData();
      htm << strdb.GetM2() << ' ';
      htm.EndTableData();
    }
    if(M3Name != 0) {
      htm.TableData();
      htm << strdb.GetM3() << ' ';
      htm.EndTableData();
    }
    if(M4Name != 0) {
      htm.TableData();
      htm << strdb.GetM4() << ' ';
      htm.EndTableData();
    }
    if(M5Name != 0) {
      htm.TableData();
      htm << strdb.GetM5() << ' ';
      htm.EndTableData();
    }
    if(M6Name != 0) {
      htm.TableData();
      htm << strdb.GetM6() << ' ';
      htm.EndTableData();
    }
    if(M7Name != 0) {
      htm.TableData();
      htm << strdb.GetM7() << ' ';
      htm.EndTableData();
    }
    if(M8Name != 0) {
      htm.TableData();
      htm << strdb.GetM8() << ' ';
      htm.EndTableData();
    }
    if(Comments != 0) {
      htm.TableData();
      htm << strdb.GetCM() << ' ';
      htm.EndTableData();
    }
    htm.EndTableRow();
    dllistptr = dllistptr->GetNext();
  }

  htm.EndTable();
  htm.Epilogue();
  
  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();
}

// 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 --------- //
// ------------------------------- //
