Listing 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "mdb.h"

static void fix_db(void);

#define LIST_RECS   1    /* Edit menu action codes */
#define NEXT        2
#define PREVIOUS    3
#define MODIFY      4
#define NEW         5
#define DELETE      6
#define UNDELETE    7
#define QUIT        8
#define SELECT      9
#define FIX        10

static struct menu_item edit_menu[] = {
   {LIST_RECS, "List Records"},
   {NEXT, "Go to Next Record"},
   {PREVIOUS, "Go to Previous Record"},
   {MODIFY, "Modify Current Record"},
   {NEW, "Add New Record"},
   {DELETE, "Delete Current Record"},
   {UNDELETE, "Un-Delete Current Record"},
   {SELECT, "Select Record (by Record Number)"},
   {FIX, "Fix Up Database"},
   {QUIT, "Return to Main Menu"},
   {'\0'}
};

/**************************************************
 * Function:       edit_db
 * Purpose:        Perform operations on current
                  Database
 * Parameter:      Database Name
 * Return Value:   None
 */

void edit_db(char *db_name) {
  int     cur_rec = 0; /* current record number */
  struct  record *rp;
  char    buf[150];
  int     i;

  while (1) {
    rp = RECS[cur_rec];
    printf("\nDatabase: %s\n", db_name);
    if (n_recs)
    {
      printf("Current Record is #%d", cur_rec);
      if (!rp->active)
          printf(" (Deleted)");
      printf(":\n");

      printf("Name:   %s %s\n", rp->first, rp->last);
      printf("ID#:    %ld\n", rp->id);
      printf("Age:    %d\n", rp->age);
      printf("Gender: %c\n", rp->gender);
      printf("Salary: $%.2f\n", rp->salary);
    }

    switch(do_menu(edit_menu, "Edit Menu")) {

       case LIST_RECS:
         for(i = 0; i < n_recs; i++)
             printf("%4d: %s%s\n", i, RECS[i]->last,
                RECS[i]->active ? "" : "(Deleted)");
         printf("Press RETURN to continue:");
         gets(buf);
         break;
       case NEXT:          /* find next active record: */
         for (i = cur_rec + 1; i < n_recs; i++)
             if (RECS[i]->active)  /*  skip inactives */
                break;
         if (i == n_recs) {  /* over the top? */
             printf("\aAt end of file.\n");
             break;
           }
         cur_rec = i;
         break;
       case PREVIOUS:   /* find previous active record: */
          for (i = cur_rec - 1; i >= 0; i--)
             if (RECS[i]->active)    /* skip inactives */
                break;
          if (i < 0) {    /* "under the bottom"? */
             printf("\aAt beginning of file.\n");
             break;
          }
          cur_rec = i;
          break;
       case NEW:
          if (n_recs+1 > max_recs) {
             printf("Maximum # of records ");
             printf("(%d) reached.\n", max_recs);
             break;
           }
           if ((rp = alloc_rec()) == NULL) {
              printf("Out of memory. Try Fixing ");
              printf("Database first...\n");
              break;
           }
                    /* make new record current: */
           cur_rec = n_recs++;
           RECS[cur_rec] = rp;
           rp->active = TRUE;
           strcpy(rp->last,""); /* initialize the record */
           strcpy(rp->first,"");
           rp->id = 0;
           rp->age = 0;
           rp->gender = ' ';
           rp->salary = 0.0F;  /* fall through to MODIFY */
       case MODIFY:
          printf("Last Name [%s]: ", rp->last);
          if (strlen(gets(buf)) > 0)
             strcpy(rp->last, buf);
          printf("First Name [%s]: ", rp->first);
          if (strlen(gets(buf)) > 0)
             strcpy(rp->first, buf);
          printf("ID# [%ld]: ", rp->id);
          if (strlen(gets(buf)) > 0)
             rp->id = atol(buf);
          printf("Age [%d] ", rp->age);
          if (strlen(gets(buf)) > 0)
             rp->age = atoi(buf);
          printf("Gender [%c]: ", rp->gender);
          if (strlen(gets(buf)) > 0)
             rp->gender = (char)toupper(*buf);
          printf("Salary [%.2f]: ", rp->salary);
          if (strlen(gets(buf)) > 0)
             rp->salary = (float) atof(buf);
          break;
       case DELETE:
          if (!rp->active) {
             printf("Record is already deleted.\n");
             break;
          }
          printf("Press 'y' to delete record,\n");
          printf("anything else to abort: ");
          gets(buf);
          if (tolower(*buf) == 'y')
             rp->active = FALSE;
          break;
       case UNDELETE:
          if (rp->active) {
             printf("Record is not deleted.\n");
             break;
          }
          rp->active = TRUE;
          printf("Record restored.\n");
          break;
       case SELECT:
          printf("Enter new record number: ");
          i = atoi(gets(buf));
          if (i < 0 || i > n_recs) {
            printf("Record # out of range.\n");
            break;
          }
          cur_rec = i;
          break;
       case FIX:
          fix_db();   /* clean up database */
          break;
       case QUIT:
          return;
    }
  }
}

/**************************************************
 *
 * Function:       fix_db
 * Purpose:        Purge deleted records, sort db
 * Parameters:     None
 * Return Value:   None
 */

static int compar(const void *a, const void *b);

static void fix_db(void) {     /* File Fix module */
   int i, new_n_recs;

   for (i = 0, new_n_recs = 0; i < n_recs; i++) {
      RECS[new_n_recs] = RECS[i];
      if (RECS[i]->active)
         new_n_recs++;
      else
         free(RECS[i]);
   }
   n_recs = new_n_recs;
   qsort(RECS, new_n_recs, sizeof(struct record *), compar);
}

/**************************************************
 * Function:       compar
 * Purpose:        Comparison function for qsort(),
 *                   sorting simply on last name
 * Parameters:     Two pointers to record pointers
 * Return Value:   As per strcmp()
 */

static int compar(const void *a, const void *b) {
   struct record **p1 = (struct record **)a;
   struct record **p2 = (struct record **)b;
   return strcmp((*p1)->last, (*p2)->last);
}