home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vbdatabs / main.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-17  |  30.9 KB  |  1,155 lines

  1. // ------------------------------- //
  2. // -------- Start of File -------- //
  3. // ------------------------------- //
  4. // ----------------------------------------------------------- // 
  5. // C++ Source Code File Name: main.cpp 
  6. // Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
  7. // Produced By: Doug Gaer  
  8. // File Creation Date: 09/18/1997  
  9. // Date Last Modified: 03/18/1999
  10. // Copyright (c) 1997 Douglas M. Gaer
  11. // ----------------------------------------------------------- // 
  12. // ------------- Program Description and Details ------------- // 
  13. // ----------------------------------------------------------- // 
  14. /*
  15. The VBD C++ classes are copyright (c) 1997, by Douglas M. Gaer.
  16. All those who put this code or its derivatives in a commercial
  17. product MUST mention this copyright in their documentation for
  18. users of the products in which this code or its derivative
  19. classes are used. Otherwise, you have the freedom to redistribute
  20. verbatim copies of this source code, adapt it to your specific
  21. needs, or improve the code and release your improvements to the
  22. public provided that the modified files carry prominent notices
  23. stating that you changed the files and the date of any change.
  24.  
  25. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  26. THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
  27. IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
  28. YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
  29. CORRECTION.
  30.  
  31. This is a test program used to test the (P)ersistent base class
  32. in a practical database application.
  33. */
  34. // ----------------------------------------------------------- // 
  35.  
  36. #include <fstream.h> // Using ofstream to print to a text file
  37. #include <ctype.h>
  38. #include "grocery.h"
  39. #include "terminal.h"
  40. #include "config.h"
  41. #include "timer.h"
  42. #include "strutil.h"
  43. #include "asprint.h"
  44. #include "pscript.h"
  45. #include "version.h"
  46. #include "groc_sh.h"
  47. #include "dbconfig.h"
  48. #include "vbdstats.h"
  49. #include "btwalker.h"
  50.  
  51. static POD *DB;                // Global database pointer
  52. const int KeyBuf = 25;         // Users input limit per string
  53. const int CBuf = 81;           // Input limit for comments
  54. const char *WildCard = "*";    // Used for wild card searches 
  55. const int retrys = 3;          // Times to retry user input operations
  56. const int StringOffset = 15;   // Display output limit per string
  57. const int IntOffset = 8;       // Display output limit per number
  58.  
  59. // Define configurable parameters
  60. int CacheSize = 15;  // Memory cache size for the index file
  61. int AdminRights = 1; // Define user privileges 
  62.  
  63. // Define file access modes used in the application
  64. VBDFile::AccessMode RWMode = VBDFile::READWRITE;
  65. VBDFile::AccessMode ROMode = VBDFile::READONLY;
  66.  
  67. // Menu menu setup
  68. typedef void (*MMF) (); // Pointer to (M)ain (M)enu (F)unctions
  69. const int MMSelections = 5; // Main menu selections
  70.  
  71. // Main menu functions
  72. void Exit();
  73. void ISRMenu();     // Insertion/Removal menu
  74. void FindMenu();    // Query menu
  75. void DisplayMenu(); // Display menu
  76. void FileMenu();    // File operations menu
  77.  
  78. const char *mainmenu[MMSelections] = {
  79.   "(0) - Exit this program",
  80.   "(1) - Add, Remove, or Change items in the database",
  81.   "(2) - Find items in the database",
  82.   "(3) - Display items in the database",
  83.   "(4) - File Operations",
  84. };
  85.  
  86. MMF MMFunctions[MMSelections] = {
  87.   &Exit,
  88.   &ISRMenu,
  89.   &FindMenu,
  90.   &DisplayMenu,
  91.   &FileMenu,
  92. };
  93.  
  94. // Insertion/Removal menu setup
  95. typedef void (*ISRMF) (); // Pointer to (A)dd (M)enu (F)unctions 
  96. const char *ISRMTitle = "--------- DATABASE INSERTION/REMOVAL MENU ---------";
  97. const int ISRMSelections = 4; // Insertion/Removal menu selections
  98.  
  99. // Insertion/Removal menu functions
  100. void Return();
  101. void Add();
  102. void Change();
  103. void Remove();
  104.  
  105. const char *isrmenu[ISRMSelections] = {
  106.   "(0) - Return to main menu",
  107.   "(1) - Add items to the database",
  108.   "(2) - Change an item in the database",
  109.   "(3) - Remove an item from the database"
  110. };
  111.  
  112. ISRMF ISRMFunctions[ISRMSelections] = {
  113.   &Return,
  114.   &Add,
  115.   &Change,
  116.   &Remove
  117. };
  118.  
  119. // Find menu setup
  120. typedef void (*FMF) (); // Pointer to (F)ind (M)enu (F)unctions 
  121. const char *FMTitle = "---------- DATABASE QUERY MENU ----------";
  122. const int FMSelections = 4; // Find menu selections
  123.  
  124. // Find menu functions
  125. void Return();
  126. void FindByName();
  127. void FindByBrand();
  128. void FindByStore();
  129.  
  130. const char *findmenu[FMSelections] = {
  131.   "(0) - Return to main menu",
  132.   "(1) - Find objects by Item Name",
  133.   "(2) - Find objects by Brand",
  134.   "(3) - Find objects by Store",
  135. };
  136.  
  137. FMF FMFunctions[FMSelections] = {
  138.   &Return,
  139.   &FindByName,
  140.   &FindByBrand,
  141.   &FindByStore,
  142. };
  143.  
  144. // Display menu setup
  145. typedef void (*DMF) (); // Pointer to (D)isplay (M)enu (F)unctions 
  146. const char *DMTitle = "---------- DATABASE DISPLAY MENU ----------";
  147. const int DMSelections = 3; // Display menu selections
  148.  
  149. // Display menu functions
  150. void DisplayLBL(); // Display all objects line by line
  151. void DisplayAll(); // Display all objects one at a time
  152.   
  153. const char *displaymenu[DMSelections] = {
  154.   "(0) - Return to main menu",
  155.   "(1) - Display all items line by line",
  156.   "(2) - Display all items one a time"
  157. };
  158.  
  159. DMF DMFunctions[DMSelections] = {
  160.   &Return,
  161.   &DisplayLBL,
  162.   &DisplayAll
  163. };
  164.  
  165. // File operations menu setup
  166. typedef void (*FOMF) (); // Pointer to (F)ile (M)enu (F)unctions 
  167. const char *FOMTitle = "---------- DATABASE FILE OPERATIONS MENU ----------";
  168. const int FOMSelections = 2; // File operations menu selections
  169.  
  170. // File operations menu functions
  171. void FileStats();
  172.  
  173. const char *filemenu[FOMSelections] = {
  174.   "(0) - Return to main menu",
  175.   "(1) - Display Variable Block Database statistics"
  176. };
  177.  
  178. FOMF FOMFunctions[FOMSelections] = {
  179.   &Return,
  180.   &FileStats
  181. };
  182.  
  183. // Function prototypes for functions use by all menu functions
  184. void AddObject(Grocery &grocery, Coords *p);
  185. void ChangeObject(Grocery &grocery, Coords *p, int adding = 0);
  186. void DisplayObject(Grocery &grocery, Coords *p);
  187. void LineByLine(Grocery &grocery, Coords *p);
  188. void DisplayString(const char *s);
  189. int LoadIndexKeys();
  190.  
  191. // Search functions
  192. void FindBy(const char *MemberName, GrocDBItem item);
  193. void GrocDBSearch(Grocery &grocery, GrocDBItem item, UString &str,
  194.          Coords *p, const char *wildcard = 0);
  195.  
  196. void MainMenu()
  197. {
  198.   int c;
  199.   Coords *p = new Coords(0, 0);
  200.  
  201.   while(1) {
  202.     p->SetXY(0, 0);
  203.     p->SetX(terminal->Center(MMTitle));
  204.     p->SetY(terminal->ScreenCenter(MMSelections));
  205.     terminal->ClearScreen();
  206.     terminal->Write(MMTitle, p->XPos(), p->YPrev());
  207.  
  208.     for(int i = 0; i < MMSelections; i++)
  209.       terminal->Write(mainmenu[i], p->XPos(), p->YOffset(1));
  210.     
  211.     terminal->Write("Press the number of your selection >",
  212.             p->XPos(), p->YOffset(2));
  213.     
  214.     c = terminal->GetChar();
  215.     switch(tolower(c)) {
  216.       case '0': 
  217.     (*MMFunctions[0]) (); // Call the appropriate menu function
  218.     return;
  219.       case '1':
  220.     (*MMFunctions[1]) (); // Call the appropriate menu function
  221.     break;
  222.       case '2':
  223.     (*MMFunctions[2]) (); // Call the appropriate menu function
  224.     break;
  225.       case '3':
  226.     (*MMFunctions[3]) (); // Call the appropriate menu function
  227.     break;
  228.       case '4':
  229.     (*MMFunctions[4]) (); // Call the appropriate menu function
  230.     break;
  231.       default:
  232.     break;
  233.     }
  234.   }
  235. }
  236.  
  237. void ISRMenu()
  238. {
  239.   int c;
  240.   Coords *p = new Coords(0, 0);
  241.  
  242.   while(1) {
  243.     p->SetXY(0, 0);
  244.     p->SetX(terminal->Center(ISRMTitle));
  245.     p->SetY(terminal->ScreenCenter(ISRMSelections));
  246.     terminal->ClearScreen();
  247.     terminal->Write(ISRMTitle, p->XPos(), p->YPrev());
  248.  
  249.     for(int i = 0; i < ISRMSelections; i++)
  250.       terminal->Write(isrmenu[i], p->XPos(), p->YOffset(1));
  251.     
  252.     terminal->Write("Press the number of your selection >",
  253.             p->XPos(), p->YOffset(2));
  254.     
  255.     c = terminal->GetChar();
  256.     switch(tolower(c)) {
  257.       case '0': 
  258.     (*ISRMFunctions[0]) (); // Call the appropriate menu function
  259.     return;
  260.       case '1':
  261.     (*ISRMFunctions[1]) (); // Call the appropriate menu function
  262.     break;
  263.       case '2':
  264.     (*ISRMFunctions[2]) (); // Call the appropriate menu function
  265.     break;
  266.       case '3':
  267.     (*ISRMFunctions[3]) (); // Call the appropriate menu function
  268.     break;
  269.       case '4':
  270.     (*ISRMFunctions[4]) (); // Call the appropriate menu function
  271.     break;
  272.       default:
  273.     break;
  274.     }
  275.   }
  276. }
  277.  
  278. void FindMenu()
  279. {
  280.   int c;
  281.   Coords *p = new Coords(0, 0);
  282.  
  283.   while(1) {
  284.     p->SetXY(0, 0);
  285.     p->SetX(terminal->Center(FMTitle));
  286.     p->SetY(terminal->ScreenCenter(FMSelections));
  287.     terminal->ClearScreen();
  288.     terminal->Write(FMTitle, p->XPos(), p->YPrev());
  289.  
  290.     for(int i = 0; i < FMSelections; i++)
  291.       terminal->Write(findmenu[i], p->XPos(), p->YOffset(1));
  292.     
  293.     terminal->Write("Press the number of your selection >",
  294.             p->XPos(), p->YOffset(2));
  295.     
  296.     c = terminal->GetChar();
  297.     switch(tolower(c)) {
  298.       case '0': 
  299.     (*FMFunctions[0]) (); // Call the appropriate menu function
  300.     return;
  301.       case '1':
  302.     (*FMFunctions[1]) (); // Call the appropriate menu function
  303.     break;
  304.       case '2':
  305.     (*FMFunctions[2]) (); // Call the appropriate menu function
  306.     break;
  307.       case '3':
  308.     (*FMFunctions[3]) (); // Call the appropriate menu function
  309.     break;
  310.       default:
  311.     break;
  312.     }
  313.   }
  314. }
  315.  
  316. void DisplayMenu()
  317. {
  318.   int c;
  319.   Coords *p = new Coords(0, 0);
  320.  
  321.   while(1) {
  322.     p->SetXY(0, 0);
  323.     p->SetX(terminal->Center(DMTitle));
  324.     p->SetY(terminal->ScreenCenter(DMSelections));
  325.     terminal->ClearScreen();
  326.     terminal->Write(DMTitle, p->XPos(), p->YPrev());
  327.  
  328.     for(int i = 0; i < DMSelections; i++)
  329.       terminal->Write(displaymenu[i], p->XPos(), p->YOffset(1));
  330.     
  331.     terminal->Write("Press the number of your selection >",
  332.             p->XPos(), p->YOffset(2));
  333.     
  334.     c = terminal->GetChar();
  335.     switch(tolower(c)) {
  336.       case '0': 
  337.     (*DMFunctions[0]) (); // Call the appropriate menu function
  338.     return;
  339.       case '1':
  340.     (*DMFunctions[1]) (); // Call the appropriate menu function
  341.     break;
  342.  
  343.       case '2':
  344.     (*DMFunctions[2]) (); // Call the appropriate menu function
  345.     break;
  346.     
  347.       default:
  348.     break;
  349.     }
  350.   }
  351. }
  352.  
  353. void FileMenu()
  354. {
  355.   int c;
  356.   Coords *p = new Coords(0, 0);
  357.  
  358.   while(1) {
  359.     p->SetXY(0, 0);
  360.     p->SetX(terminal->Center(FOMTitle));
  361.     p->SetY(terminal->ScreenCenter(FOMSelections));
  362.     terminal->ClearScreen();
  363.     terminal->Write(FOMTitle, p->XPos(), p->YPrev());
  364.  
  365.     for(int i = 0; i < FOMSelections; i++)
  366.       terminal->Write(filemenu[i], p->XPos(), p->YOffset(1));
  367.     
  368.     terminal->Write("Press the number of your selection >",
  369.             p->XPos(), p->YOffset(2));
  370.     
  371.     c = terminal->GetChar();
  372.     switch(tolower(c)) {
  373.       case '0': 
  374.     (*FOMFunctions[0]) (); // Call the appropriate menu function
  375.     return;
  376.       case '1':
  377.     (*FOMFunctions[1]) (); // Call the appropriate menu function
  378.     break;
  379.  
  380.       default:
  381.     break;
  382.     }
  383.   }
  384. }
  385.  
  386. void Exit()
  387. {
  388.   // Nothing to do
  389. }
  390.  
  391. void Return()
  392. {
  393.   // Nothing to do
  394. }
  395.  
  396. void LoadKeys(EntryKey &e)
  397. // Visit function used to load the btree keys into memory
  398. {
  399.   InMemCopy inmemcopy(e.key, e.object_address, e.class_id);
  400.   dllist->StoreNode(inmemcopy);
  401. }
  402.  
  403. int LoadIndexKeys()
  404. {
  405.   // Clear the doubly linked list
  406.   dllist->Clear();
  407.  
  408.   BtreeWalker btw(DB->Index());
  409.   unsigned num_objects = btw.Sort(LoadKeys);
  410.  
  411.   return num_objects;
  412. }
  413.  
  414. void DisplayAll()
  415. {
  416.   if(DB->RebuildIndex()) {
  417.     terminal->Write("The index file needs to be rebuilt.", 0, 1);
  418.     terminal->AnyKey(0, 2);
  419.     return;
  420.   }
  421.  
  422.   Grocery grocery(DB);
  423.   Coords *p = new Coords(0, 0);
  424.   int c;
  425.  
  426.   terminal->ClearScreen();      
  427.   LoadIndexKeys();
  428.   
  429.   dllistptr = dllist->GetFront();
  430.   while(!dllist->IsHeader(dllistptr)) {
  431.     terminal->ClearScreen();      
  432.     p->SetXY(0, 0); // Reset x and y positions
  433.     grocery.ReadObject(dllistptr->Data.object_address);
  434.     DisplayObject(grocery, p);
  435.     terminal->Write("(C)-Change, (D)-Delete, (X)-Exit",
  436.             p->XPos(), p->YOffset(2));
  437.     terminal->Write(", Any other key to continue");
  438.     c = terminal->GetChar();
  439.     switch(tolower(c)) {
  440.       case 'c': 
  441.     terminal->ClearScreen();
  442.     p->SetXY(0, 0);
  443.     ChangeObject(grocery, p);
  444.     break;
  445.     
  446.       case 'd': {
  447.     int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
  448.                  p->XPos(), p->YOffset(2));
  449.     if(!yn) break;
  450.     grocery.DeleteObject();
  451.     break;
  452.       }
  453.       
  454.       case 'x':
  455.     dllist->Clear();
  456.     delete p;
  457.     return;
  458.  
  459.       default:
  460.     break;
  461.     }
  462.     dllistptr = dllistptr->GetNext(); 
  463.   }
  464. }
  465.  
  466. void ItemBar(Coords *p)
  467. {
  468.   const char *col1 = "Name";
  469.   const char *col2 = "Brand";
  470.   const char *col3 = "Store";
  471.   const char *col4 = "Price";
  472.   const char *col5=  "Qty.";
  473.   const char *col6 = "Line Total";
  474.   
  475.   int x = p->XPos(); 
  476.   terminal->Write(col1, p->XPos(), p->YPos());
  477.   terminal->Write(col2, p->XOffset(StringOffset+1), p->YPos());
  478.   terminal->Write(col3, p->XOffset(StringOffset+1), p->YPos());
  479.   terminal->Write(col4, p->XOffset(StringOffset+1), p->YPos());
  480.   terminal->Write(col5, p->XOffset(IntOffset+1), p->YPos());
  481.   terminal->Write(col6, p->XOffset(IntOffset+1), p->YPos());
  482.   terminal->MoveCursor(0, p->YOffset(1));
  483.   int i = 0;
  484.   while (i++ < terminal->MaxCols()-1) terminal->Write('=');
  485.   p->YOffset(1);
  486.   p->SetX(x);
  487. }
  488.  
  489. void DisplayLBL()
  490. {
  491.   if(DB->RebuildIndex()) {
  492.     terminal->Write("The index file needs to be rebuilt.", 0, 1);
  493.     terminal->AnyKey(0, 2);
  494.     return;
  495.   }
  496.  
  497.   Grocery grocery(DB);
  498.   int c;
  499.   unsigned count = 0;
  500.   Coords *p = new Coords(0, 0);
  501.   terminal->ClearScreen();
  502.   ItemBar(p);
  503.  
  504.   LoadIndexKeys();
  505.  
  506.   dllistptr = dllist->GetFront();
  507.   while(!dllist->IsHeader(dllistptr)) {
  508.     // Display the first object
  509.     grocery.ReadObject(dllistptr->Data.object_address);
  510.     LineByLine(grocery, p);
  511.     p->YOffset(1);
  512.     count++;
  513.     dllistptr = dllistptr->GetNext(); 
  514.     if(count > 5) {
  515.     terminal->Write("(X)-Exit", p->XPos(), p->YOffset(2));
  516.     terminal->Write(", Any other key to continue");
  517.     c = terminal->GetChar();
  518.     switch(tolower(c)) {
  519.       case 'x':
  520.     dllist->Clear();
  521.     delete p; // Free Coords pointer
  522.     return;
  523.     
  524.       default:
  525.     break;
  526.     }
  527.     terminal->ClearScreen();
  528.     p->SetXY(0, 0);
  529.     ItemBar(p);
  530.     count = 0;
  531.     }
  532.   }
  533.  
  534.   terminal->AnyKey(p->XPos(), p->YOffset(2));
  535.   dllist->Clear();
  536.   delete p; // Free Coords pointer
  537. }
  538.  
  539. void Remove()
  540. {
  541.   if(!AdminRights) {
  542.     terminal->ClearScreen();
  543.     terminal->Write("You do not have Admin User Privileges", 0, 1);
  544.     terminal->AnyKey(0, 2);
  545.     return;
  546.   }
  547.  
  548.   terminal->ClearScreen();
  549.   Coords *p = new Coords(0, 0);
  550.   char buf[KeyBuf]; 
  551.   Grocery grocery(DB);
  552.  
  553.   terminal->Write("Enter the name of the item to delete: ",
  554.           p->XPos(), p->YPos());
  555.   terminal->GetString(buf);
  556.   grocery.SetName(buf);
  557.  
  558.   FAU addr = grocery.FindObject();
  559.   
  560.   if(!addr) { 
  561.     terminal->Write("Could not find: ", p->XPos(), p->YOffset(2));
  562.     terminal->Write(buf);
  563.     terminal->AnyKey(p->XPos(), p->YOffset(2));
  564.     delete p; // Free the Coord pointer
  565.     return;
  566.   }
  567.   int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
  568.                p->XPos(), p->YOffset(2));
  569.   if(!yn) {
  570.     delete p; // Free Coord pointer
  571.     return;
  572.   }
  573.  
  574.   grocery.DeleteObject();
  575.   
  576.   terminal->Write("Deleted item ", p->XPos(), p->YOffset(2));
  577.   terminal->Write(buf);
  578.  
  579.   terminal->AnyKey(p->XPos(), p->YOffset(2));
  580.   delete p; // Free Coord pointer
  581. }
  582.  
  583. void FileStats()
  584. {
  585.   VBDStats(terminal, DB->OpenDatabase());
  586.   VBDStats(terminal, DB->OpenIndexFile());
  587. }
  588.  
  589. void Add()
  590. {
  591.   if(!AdminRights) {
  592.     terminal->ClearScreen();
  593.     terminal->Write("You do not have Admin User Privileges", 0, 1);
  594.     terminal->AnyKey(0, 2);
  595.     return;
  596.   }
  597.  
  598.   int r = retrys;
  599.   
  600.   Coords *p = new Coords(0, 0);
  601.   char buf[KeyBuf];
  602.   terminal->ClearScreen();
  603.  
  604.   while(1) {
  605.     terminal->Write("Enter the name of item to add: ", p->XPos(), p->YPos());
  606.     terminal->GetString(buf);
  607.     if(!*buf) r--; else break;
  608.     if(!r) {
  609.       delete p;
  610.       return;
  611.     }
  612.   }
  613.  
  614.   Grocery grocery(DB);
  615.   UString key_buf(buf);
  616.  
  617.   // Remove any leading space from the key name entry
  618.   int offset = key_buf.Find(" ");
  619.   if(offset == 0) key_buf.DeleteAt(offset, 1);
  620.     
  621.   if(key_buf == "") { // Check for a key name valid input
  622.     delete p;
  623.     return;
  624.   }
  625.  
  626.   grocery.SetName(key_buf);
  627.   FAU addr = grocery.FindObject();
  628.    
  629.   if(addr) { 
  630.     terminal->Write(buf, p->XPos(), p->YOffset(2));
  631.     terminal->Write(" entry already exists."); 
  632.     terminal->AnyKey(p->XPos(), p->YOffset(2));
  633.     delete p; // Free Coords pointer
  634.     return;
  635.   }
  636.  
  637.   AddObject(grocery, p);
  638.   delete p; // Free Coords pointer
  639. }
  640.  
  641. void AddObject(Grocery &grocery, Coords *p)
  642. {
  643.   char buf[KeyBuf];
  644.   double price = 0;
  645.   int quantity = 0;
  646.   char purchasing = ' ';
  647.   double total = 0;
  648.  
  649.   terminal->Write("Enter the brand name: ",
  650.                   p->XPos(), p->YOffset(1));
  651.   terminal->GetString(buf);
  652.   grocery.SetBrand(buf);
  653.   terminal->Write("Enter the name of the store: ",
  654.                   p->XPos(), p->YOffset(1));
  655.   terminal->GetString(buf);
  656.   grocery.SetStore(buf);
  657.   terminal->Write("Enter the item's price: $",
  658.                   p->XPos(), p->YOffset(1));
  659.   price = terminal->GetFloat();
  660.   grocery.SetPrice(price);
  661.   terminal->Write("Enter the quantity: ", p->XPos(), p->YOffset(1));
  662.   quantity = terminal->GetInt();
  663.   grocery.SetQuantity(quantity);
  664.   terminal->Write("Purchasing (y/n): ", p->XPos(), p->YOffset(1));
  665.   if(terminal->GetYesNo()) purchasing = 'Y'; else purchasing = 'N';
  666.   grocery.SetPurchasing(purchasing);
  667.   FLOAT64 LineTotal = grocery.GetPrice() * grocery.GetQuantity();
  668.   grocery.SetLineTotal(LineTotal);
  669.   terminal->Write("Line total = $", p->XPos(), p->YOffset(1));
  670.   terminal->Write(grocery.GetLineTotal());
  671.  
  672.   terminal->Write("(A)-Abort (C)-Change, Any other key to accept entry",
  673.           p->XPos(), p->YOffset(2));
  674.   int c = terminal->GetChar();
  675.   switch(c) {
  676.     case 'c': case 'C': 
  677.       terminal->ClearScreen();
  678.       p->SetXY(0, 0);
  679.       ChangeObject(grocery, p, 1); 
  680.       break;
  681.     case 'a':
  682.       return;
  683.     default:
  684.       break;
  685.   }
  686.  
  687.   int find = 0;
  688.   FAU addr = grocery.AddObject(find); // Write the object to the file
  689.   if(!addr) {
  690.     terminal->Write("Could not add item to database",
  691.                     p->XPos(), p->YOffset(2));
  692.     terminal->AnyKey(p->XPos(), p->YOffset(2));
  693.     return;
  694.   }
  695.   
  696.   terminal->Write("Item was added to the database",
  697.                   p->XPos(), p->YOffset(2));
  698.  
  699.   terminal->Write("Add another (y/n)", p->XPos(), p->YOffset(2));
  700.   if(terminal->GetYesNo()) Add(); else return; 
  701. }
  702.  
  703. void FindBy(const char *MemberName, GrocDBItem item)
  704. {
  705.   Coords *p = new Coords(0, 0);
  706.   char buf[KeyBuf]; 
  707.   unsigned offset = 0;
  708.  
  709.   terminal->ClearScreen();
  710.   
  711.   Grocery grocery(DB);
  712.  
  713.   terminal->Write("Enter complete name or use ", p->XPos(), p->YPos());
  714.   terminal->Write(WildCard);
  715.   terminal->Write(" for a wild card.");
  716.   terminal->Write(MemberName, p->XPos(), p->YOffset(2));
  717.   terminal->Write(" -> ");
  718.   terminal->GetString(buf);
  719.   UString str(buf);
  720.   offset = str.Find(WildCard, offset); // Look for wild card character
  721.  
  722.   if(DB->RebuildIndex()) {
  723.     terminal->Write("The index file needs to be rebuilt.",
  724.             p->XPos(), p->YOffset(2));
  725.     terminal->AnyKey(p->XPos(), p->YOffset(1));
  726.     delete p;
  727.     return;
  728.   }
  729.   
  730.   if(offset == UString::NoMatch) { // No wild cards found
  731.     grocery.SetName(buf);
  732.     GrocDBSearch(grocery, item, str, p);
  733.     delete p; // Free the Coord pointer
  734.     return;
  735.   }
  736.  
  737.   GrocDBSearch(grocery, item, str, p, WildCard);
  738.   delete p; // Free the Coord pointer
  739. }
  740.  
  741. void FindByName()
  742. {
  743.   FindBy("Item Name", NAME);
  744. }
  745.  
  746. void FindByBrand()
  747. {
  748.   FindBy("Brand Name", BRAND);
  749. }
  750.  
  751. void FindByStore()
  752. {
  753.   FindBy("Store Name", STORE);
  754. }
  755.  
  756. void Change()
  757. {
  758.   if(!AdminRights) {
  759.     terminal->ClearScreen();
  760.     terminal->Write("You do not have Admin User Privileges", 0, 1);
  761.     terminal->AnyKey(0, 2);
  762.     return;
  763.   }
  764.  
  765.   Coords *p = new Coords(0, 0);
  766.   char buf[KeyBuf];
  767.   
  768.   terminal->ClearScreen();
  769.   terminal->Write("Enter name of item to change: ", p->XPos(), p->YPos());
  770.   terminal->GetString(buf);
  771.  
  772.   Grocery grocery(DB);
  773.   grocery.SetName(buf);
  774.  
  775.   FAU addr = grocery.FindObject();
  776.    
  777.   if(!addr) { 
  778.     terminal->Write("Could not find: ", p->XPos(), p->YOffset(2));
  779.     terminal->Write(buf);
  780.     terminal->AnyKey(p->XPos(), p->YOffset(2));
  781.     delete p; // Free Coords pointer
  782.     return;
  783.   }
  784.  
  785.   ChangeObject(grocery, p);
  786.   delete p; // Free Coords pointer
  787. }
  788.  
  789. void ChangeObject(Grocery &grocery, Coords *p, int adding)
  790. {
  791.   char buf[KeyBuf];
  792.  
  793.   // Buffer the object's data to allow changes to be canceled
  794.   Grocery ob;
  795.   ob.Copy(grocery);
  796.   FAU addr;
  797.   
  798.   // Temp buffers used to record changes
  799.   Price pr = 0;
  800.   Quantity qty = 0;
  801.   Purchasing pur = ' ';
  802.   FLOAT64 LineTotal;
  803.   Grocery changed(DB);
  804.   
  805.   char *prompt = "Press the number of your selection >";  
  806.   int c, x, y, x_prev, y_prev, x_prompt, y_prompt;
  807.   x_prev = p->XPos(); y_prev = p->YPos();
  808.  
  809.   while(1) {
  810.     p->SetXY(x_prev, y_prev);
  811.     terminal->Write("(0) Cancel change", p->XPos(), p->YOffset(2));
  812.     if(adding) {
  813.     terminal->Write("(1) Item's Name  =  ", p->XPos(), p->YOffset(1));
  814.     terminal->Write(grocery.GetName());
  815.     terminal->ClearLine();
  816.     }
  817.     else {
  818.     terminal->Write("(-) Item's Name  =  ", p->XPos(), p->YOffset(1));
  819.     terminal->Write(grocery.GetName());
  820.     terminal->ClearLine();
  821.     }
  822.     terminal->Write("(2) Brand        =  ", p->XPos(), p->YOffset(1));
  823.     terminal->Write(grocery.GetBrand());
  824.     terminal->ClearLine();
  825.     terminal->Write("(3) Store        =  ", p->XPos(), p->YOffset(1));
  826.     terminal->Write(grocery.GetStore());
  827.     terminal->ClearLine();
  828.     terminal->Write("(4) Price        = $", p->XPos(), p->YOffset(1));
  829.     terminal->Write(grocery.GetPrice());
  830.     terminal->ClearLine();
  831.     terminal->Write("(5) Quantity     =  ", p->XPos(), p->YOffset(1));
  832.     terminal->Write(grocery.GetQuantity());
  833.     terminal->ClearLine();
  834.     terminal->Write("(6) Purchasing   =  ", p->XPos(), p->YOffset(1));
  835.     terminal->Write(grocery.GetPurchasing());
  836.     terminal->ClearLine();
  837.     terminal->Write("(-) Total        =  ", p->XPos(), p->YOffset(1));
  838.     terminal->Write(grocery.GetLineTotal());
  839.     terminal->ClearLine();
  840.     terminal->Write("(A) Accept changes", p->XPos(), p->YOffset(1));
  841.  
  842.     terminal->Write(prompt, p->XPos(), p->YOffset(2));
  843.     x_prompt = p->XPos() + strlen(prompt);
  844.     y_prompt = p->YPos();
  845.   
  846.     x = p->XPos();
  847.     y = p->YPos() + 2;
  848.  
  849.     c = terminal->GetChar();
  850.     switch(tolower(c)) {
  851.       case '0': 
  852.     grocery.Copy(ob);
  853.     return;
  854.  
  855.       case '1':
  856.     // User not allowed to change name unless adding a new object
  857.     if(!adding) break; 
  858.     terminal->Write("Enter new name: ", x, y);
  859.     terminal->GetString(buf);
  860.     grocery.SetName(buf);
  861.     if(grocery.FindObject()) {
  862.       terminal->Write("Item already exists", x, y+1);
  863.       terminal->AnyKey();
  864.       terminal->ClearLine(x, y+1);
  865.       terminal->ClearLine(x, y);
  866.       terminal->MoveCursor(x_prev, y_prev);
  867.       break;
  868.     }
  869.     terminal->ClearLine(x, y);
  870.     terminal->MoveCursor(x_prev, y_prev);
  871.     break;
  872.  
  873.       case '2':
  874.     terminal->Write("Enter new brand: ", x, y);
  875.     terminal->GetString(buf);
  876.     grocery.SetBrand(buf);
  877.     terminal->ClearLine(x, y);
  878.     terminal->MoveCursor(x_prev, y_prev);
  879.     break;
  880.  
  881.       case '3':
  882.     terminal->Write("Enter new store: ", x, y);
  883.     terminal->GetString(buf);
  884.     grocery.SetStore(buf);
  885.     terminal->ClearLine(x, y);
  886.     terminal->MoveCursor(x_prev, y_prev);
  887.     break;
  888.  
  889.       case '4':
  890.     terminal->Write("Enter new price: $", x, y);
  891.     pr = terminal->GetFloat();
  892.     grocery.SetPrice(pr);
  893.     terminal->ClearLine(x, y);
  894.     terminal->MoveCursor(x_prev, y_prev);
  895.     LineTotal = grocery.GetPrice() * grocery.GetQuantity();
  896.     grocery.SetLineTotal(LineTotal);
  897.     break;
  898.  
  899.       case '5':
  900.     terminal->Write("Enter new quantity: ", x, y);
  901.     qty = terminal->GetInt();
  902.     grocery.SetQuantity(qty);
  903.     terminal->ClearLine(x, y);
  904.     terminal->MoveCursor(x_prev, y_prev);
  905.     LineTotal = grocery.GetPrice() * grocery.GetQuantity();
  906.     grocery.SetLineTotal(LineTotal);
  907.     break;
  908.  
  909.       case '6':
  910.     terminal->Write("Purchasing (y/n): ", x, y);
  911.         if(terminal->GetYesNo()) pur = 'Y'; else pur = 'N'; 
  912.         grocery.SetPurchasing(pur);
  913.     terminal->ClearLine(x, y);
  914.     terminal->MoveCursor(x_prev, y_prev);
  915.     break;
  916.  
  917.       case '7':
  918.     break;
  919.     
  920.       case 'A': case 'a':
  921.     if(adding) return; // New object, no need to reallocate space
  922.     if(grocery.FullCompare(ob)) return; // The object has not changed
  923.     changed.Copy(grocery);  // Make a copy of the changes
  924.  
  925.     // Remove the original object 
  926.     grocery.DeleteObject();
  927.     changed.AddObject(0); // Add the changed object back to the database
  928.     return;
  929.  
  930.       default:
  931.     break;
  932.     }
  933.   }
  934. }
  935.  
  936. void DisplayObject(Grocery &grocery, Coords *p)
  937. // Display's the complete object with all details.
  938. {
  939.   terminal->Write("Item's Name  =  ", p->XPos(), p->YPos());
  940.   terminal->Write(grocery.GetName());
  941.   terminal->Write("Brand        =  ", p->XPos(), p->YOffset(1));
  942.   terminal->Write(grocery.GetBrand());
  943.   terminal->Write("Store        =  ", p->XPos(), p->YOffset(1));
  944.   terminal->Write(grocery.GetStore());
  945.   terminal->Write("Price        = $", p->XPos(), p->YOffset(1));
  946.   terminal->Write(grocery.GetPrice());
  947.   terminal->Write("Quantity     =  ", p->XPos(), p->YOffset(1));
  948.   terminal->Write(grocery.GetQuantity());
  949.   terminal->Write("Purchasing   =  ", p->XPos(), p->YOffset(1));
  950.   terminal->Write(grocery.GetPurchasing());
  951.   terminal->Write("Line total   =  ", p->XPos(), p->YOffset(1));
  952.   terminal->Write(grocery.GetLineTotal());
  953. }
  954.  
  955. void DisplayString(const char *s)
  956. {
  957.   int len = strlen(s);
  958.   int offset = StringOffset;
  959.   char *buf = new char[len + 1];
  960.   buf[len] = '\0';
  961.   strcpy(buf, s);
  962.   if(len < StringOffset) offset = len;
  963.   for(int i = 0; i < offset; i++)
  964.     terminal->Write(buf[i]);
  965.   delete[] buf;
  966. }
  967.  
  968. void LineByLine(Grocery &grocery, Coords *p)
  969. // Display objects line by line with partial details
  970. {
  971.   int x = p->XPos();
  972.  
  973.   terminal->MoveCursor(p->XPos(), p->YPos());
  974.   DisplayString(grocery.GetName());
  975.   terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos()); 
  976.   DisplayString(grocery.GetBrand());
  977.   terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos()); 
  978.   DisplayString(grocery.GetStore());
  979.   terminal->MoveCursor(p->XOffset(StringOffset+1), p->YPos()); 
  980.   terminal->Write(grocery.GetPrice());
  981.   terminal->MoveCursor(p->XOffset(IntOffset+1), p->YPos()); 
  982.   terminal->Write(grocery.GetQuantity());
  983.   terminal->MoveCursor(p->XOffset(IntOffset+1), p->YPos()); 
  984.   terminal->Write(grocery.GetLineTotal());
  985.   int i = 0;
  986.   terminal->MoveCursor(0, p->YOffset(1));
  987.   while (i++ < terminal->MaxCols()-1) terminal->Write('-');
  988.   p->SetX(x);
  989. }
  990.  
  991. void Version()
  992. {
  993.   printf("\n%s program version %.3f\n", ProgramName, VersionNumber);
  994.   exit(0);
  995. }
  996.  
  997. void GrocDBSearch(Grocery &grocery, GrocDBItem item, UString &str,
  998.           Coords *p, const char *wildcard)
  999. {
  1000.   terminal->Write("Searching for ", p->XPos(), p->YOffset(2));
  1001.   terminal->Write(str.c_str());
  1002.  
  1003.   int offset;
  1004.   offset = ObjectsFound = 0;
  1005.  
  1006.   if(wildcard != 0) {
  1007.     while(1) { // Remove the wild card characters from the string
  1008.       offset = str.Find(wildcard, offset);
  1009.       if (offset == UString::NoMatch) break;
  1010.       str.DeleteAt(offset, strlen(wildcard));
  1011.       offset++;
  1012.     }
  1013.   }
  1014.  
  1015.   // stay in sync during multiple file access.
  1016.   DB->Index()->TestTree();
  1017.  
  1018.   dllist->Clear();
  1019.   
  1020.   if(wildcard != 0)
  1021.     BtreeSearch(DB->Index(), item, grocery, str, 1);
  1022.   else
  1023.     BtreeSearch(DB->Index(), item, grocery, str);
  1024.  
  1025.   if(ObjectsFound == 0) {
  1026.     terminal->Write("No matches found.", p->XPos(), p->YOffset(2));
  1027.     terminal->AnyKey(p->XPos(), p->YOffset(1));
  1028.     return;
  1029.   }
  1030.  
  1031.   if(ObjectsFound > 1) { // Found multiple matches
  1032.     terminal->Write("Found ", p->XPos(), p->YOffset(2));
  1033.     terminal->Write(ObjectsFound);
  1034.     terminal->Write(" matching.");
  1035.     terminal->AnyKey(p->XPos(), p->YOffset(1));
  1036.   }
  1037.   else {
  1038.     terminal->Write("Found matching entry for:  ", p->XPos(), p->YOffset(2));
  1039.     terminal->Write(str.c_str());
  1040.     terminal->AnyKey(p->XPos(), p->YOffset(1));
  1041.   }
  1042.  
  1043.   // Display all the matches
  1044.   dllistptr = dllist->GetFront();
  1045.   while(!dllist->IsHeader(dllistptr)) {
  1046.     terminal->ClearScreen();
  1047.     p->SetXY(0, 0);
  1048.     grocery.ReadObject(dllistptr->Data.object_address);
  1049.     DisplayObject(grocery, p);
  1050.     terminal->Write("(C)-Change, (D)-Delete, (X)-Exit",
  1051.             p->XPos(), p->YOffset(2));
  1052.     terminal->Write(", Any other key to continue");
  1053.     int c = terminal->GetChar();
  1054.     switch(c) {
  1055.       case 'c': case 'C': 
  1056.     terminal->ClearScreen();
  1057.     p->SetXY(0, 0);
  1058.     grocery.ReadObject(dllistptr->Data.object_address);
  1059.         ChangeObject(grocery, p);
  1060.     break;
  1061.     
  1062.       case 'd': case 'D': {
  1063.     int yn = terminal->YesNo("Are you sure you want to delete (y/n)",
  1064.                  p->XPos(), p->YOffset(2));
  1065.     if(!yn) break;
  1066.     grocery.ReadObject(dllistptr->Data.object_address);
  1067.         grocery.DeleteObject();
  1068.     break;
  1069.       }
  1070.       
  1071.       case 'x': case 'X':
  1072.     dllist->Clear(); // Free dllist memory
  1073.     return;
  1074.  
  1075.       default:
  1076.     break;
  1077.     }
  1078.     dllistptr = dllistptr->GetNext();
  1079.   }
  1080.   dllist->Clear(); // Free dllist memory
  1081. }
  1082.  
  1083. int main(int argc, char **argv)
  1084. {
  1085.   // Display the program version information and exit the program
  1086.   if(argc >= 2) {
  1087.     if(strcmp(argv[1], "version") == 0) Version();
  1088.   }
  1089.  
  1090.   char *FileName;
  1091.   char *CurrentCfgFile;
  1092.   char *CfgFile;
  1093.  
  1094.   Config *CfgData = new Config;
  1095.   char *AdminUser = 0;
  1096.   
  1097.   // Look for CfgFile name in environment
  1098.   if((CurrentCfgFile = getenv("GDBCFG")) == 0)
  1099.     CfgFile = (char *)DefaultCfgFile;
  1100.   else
  1101.     CfgFile = CurrentCfgFile;
  1102.  
  1103.   // Use default file of CFG file name if no file is specifed on 
  1104.   if(argc < 2) {
  1105.     int FileStatus = CfgData->Load((char *)CfgFile);
  1106.     if(!FileStatus)
  1107.       FileName = (char *)DefaultDBFileName;
  1108.     else {
  1109.       FileName = CfgData->GetStrValue("DBFileName");
  1110.       AdminUser = CfgData->GetStrValue("AdminUser");
  1111.       if(!FileName) {
  1112.     printf("\nDBFileName section missing in config file: %s\n", CfgFile);
  1113.     exit(0);
  1114.       }
  1115.     }
  1116.   }
  1117.   else {
  1118.     FileName = argv[1];
  1119.   }
  1120.  
  1121.   if(!AdminUser) {
  1122.     AdminRights = 1;
  1123.   }
  1124.   else {
  1125.     int result = strcmp(AdminUser, "FALSE");
  1126.     if(result == 0) {
  1127.       AdminRights = 0;
  1128.     }
  1129.     else {
  1130.       AdminRights = 1;
  1131.     }
  1132.   }
  1133.  
  1134.   CfgData->UnLoad(); // Unload the Config file from memory
  1135.  
  1136.   // Initialize global database pointer
  1137.   // NOTE: Index file and data file will share the same file name
  1138.   // with different file extensions. The data file will have a 
  1139.   // .pod extension and the index file will have a .btx extension.
  1140.   if(AdminRights == 1) 
  1141.     DB = new POD(FileName, RWMode, 1, CacheSize);
  1142.   else
  1143.     DB = new POD(FileName, ROMode, 1, CacheSize);
  1144.  
  1145.   terminal->init();
  1146.   MainMenu();
  1147.   terminal->finish();
  1148.   delete DB;
  1149.   return 0;
  1150. }
  1151. // ----------------------------------------------------------- //
  1152. // ------------------------------- //
  1153. // --------- End of File --------- //
  1154. // ------------------------------- //
  1155.