-2

I'm revising a small program, I have successfully made this small program into a larger one, but I still need two functions.

  1. have an EDIT option
  2. return multiple records upon searching.

I am helping a friend of mine.

#include <stdio.h>


#define CAPACITY 100

/* User commands */

#define ADD 'a'
#define FIND   'f'
#define LIST   'l'
#define QUIT   'q'

#define NAME_LEN 80
#define NUMBER_LEN 40

/* In memory representation of an entry in the phonebook. */

typedef struct {
  char name[ NAME_LEN ];
  char number[ NUMBER_LEN ];
} phone_record;

/* Procedures in phonebook.c */

char get_command( void );
phone_record *get_record( void );
void add_record( phone_record *new_record );
void list_phonebook( void );
int find_name( char *name );

int num_entries;           // Number of entries currently in the phone book
phone_record **phonebook;  // Where the names are stored

int main( int argc, char **argv ) {
  char ch;
  char name[ NAME_LEN ];
  char confirm[ 10 ];
  phone_record *rec;
  int loc;

  // Create an empty phonebook
  phonebook = (phone_record **)malloc( sizeof( phone_record *) * CAPACITY );
  num_entries = 0;

  // Read commands until the user gets tired
  while ( ( ch = get_command() ) != QUIT ) {
    switch( ch ) {
      case ADD:
        // Get new info
        rec = get_record();

          add_record( rec );

        break;

      case FIND:
        // Name to find
        printf( "Name:  " );
        scanf( "%s", name );

        // Look for the name
        if ( ( loc = find_name( name ) ) != -1 ) {
          printf( "Number:  %s\n", phonebook[ loc ]->number );
        }
        else {
          printf( "That name is not in the phonebook\n" );
        }
        break;

      case LIST:
        // List the phonebook
        list_phonebook();
        break;
    }
  }
}

/* 
 * Read and return a command from the keyboard. 
 */
char get_command() {
  char line[ 80 ];

  do {
    // Get input
    printf( "pb> " );

    // scanf returns -1 when it encoutners EOF - pretend we saw quit
    if ( scanf( "%s", line ) == -1 ) {
      line[ 0 ] = QUIT;
      printf( "\n" );  // Add new line so terminal looks nice
    }

    // Verify input (lightly)
    switch( line[ 0 ] ) {
      case ADD:
      case FIND:
      case LIST:
      case QUIT:
        break;
      default:
        printf( "Unrecognized command\n" );
        line[ 0 ] = 0;
    }
  } while ( line[ 0 ] == 0 );

  return line[ 0 ];
}

/*
 * Add a new record to the phonebook.
 */
void add_record( phone_record *new_record ) {
  int cur;

  // Make sure there is room
  if ( num_entries == CAPACITY ) {
    printf( "Sorry phonebook is full\n" );
  }
  else {
    // Insertion sort.  Start at bottom and copy elements down one until
    // you find the first one less than what we are adding or we hit the
    // top of the phonebook
    for ( cur = num_entries; 
          cur > 0 && strcmp( phonebook[ cur - 1 ]->name, new_record->name ) > 0;
          cur = cur - 1 ) {

      phonebook[ cur ] = phonebook[ cur - 1 ];
    }

    // Add the entry in the open slot
    phonebook[ cur ] = new_record;
    num_entries = num_entries + 1;
  }
}

/*
 * List the entries in the phonebook.
 */
void list_phonebook() {
  int i;

  if ( num_entries != 0 ) {
    printf( "Name\t\tNumber\n" );
    printf( "----\t\t------\n" );

    for ( i = 0; i < num_entries; i = i + 1 ) {
      printf( "%s\t\t%s\n", phonebook[ i ]->name, phonebook[ i ]->number );
    }
  }
  else {
    printf( "There are no entries in the phonebook\n" );
  }
}


/*
 * Find a name in the phonebook.  -1 means it is not there.
 */
int find_name( char *name ) {
  int pos = -1;
  int i;

  for ( i = 0; pos == -1 && i < num_entries; i = i + 1 ) {
    if ( strcmp( name, phonebook[ i ]->name ) == 0 ) pos = i;
  }  

  return pos;
}

/*
 * Read and return a phone record from the keyboard.
 */
phone_record *get_record() {
  phone_record *rec;
  char *name;
  char *number;

  // Allocate storage for the phone record.  Since we want the record
  // to live after the function returns we need to use malloc
  rec = (phone_record *)malloc( sizeof( phone_record ) );

  // Get the data
  printf( "Name:  " );
  scanf( "%s", rec->name );

  printf( "Phone:  " );
  scanf( "%s", rec->number );

  return rec;
}

The find_name() function which locates a matching name in the phonebook array is:

int find_name( char *name ) {
  int pos = -1;
  int i;

  for ( i = 0; pos == -1 && i < num_entries; i = i + 1 ) {
    if ( strcmp( name, phonebook[ i ]->name ) == 0 ) pos = i;
  }  

  return pos;
}

it only returns one integer showing the position of a matching element of phonebook:

if ( ( loc = find_name( name ) ) != -1 ) {
  printf( "Number:  %s\n", phonebook[ loc ]->number );
}

I'm thinking that the method find_name should return an array instead, but I don't know how to implement that.

halfer
  • 19,824
  • 17
  • 99
  • 186
Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
  • An array *of what* ?? Also, I see no reason your global phone-book is dynamic, much less each *entry* is dynamic, as you enforce a top-end limit of `CAPACITY` already. And [**don't cast `malloc` in C programs**](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – WhozCraig Oct 06 '14 at 14:17
  • array of positions, which will be passed to 'loc' to be printed. – Glenn Posadas Oct 06 '14 at 14:48

2 Answers2

1

Probably easiest to return an array of integers using a negative one (-1) to indicate the end of the list of integers.

So your function would be modified to something like the following. I have not actually compiled this so you may need to fix some things.

int find_name( char *name, int *iList, int nListSize ) {
  int pos = -1;
  int i;
  int j;

  for ( j = i = 0; i < num_entries; i = i + 1 ) {
    if ( strcmp( name, phonebook[ i ]->name ) == 0 ) {
        if (j + 1 < nListSize)
            iList[j++] = i;
    }
  }  

  iList[j] = -1;      // indicate the end of the list
  if (j < nListSize) pos = j;  // return the number of items in the list
  return pos;
}

You will need to modify the way you are using this function to create an array of integers and to pass the array along with the max number of array entries. And to then check the return value to determine how many, if any, matches were found.

The function returns a -1 if there was an error of more matches found than there are array elements available to put them into.

Edit

in the case FIND: you would do an output loop something like the following. Again I have not done a compile or testing however this is the concept. And you do have an issue if there are more than 100 matching names with this example so you need to think about how to handle that eventuality:

// Look for the name
{
    int   aList[100];   // just a large array and hope no more than this matching entries
    if ( ( loc = find_name( name, aList, 100 ) ) > 0 ) {
        int iLoop = 0;
        // loop through the list to print out the matching entries.
        for (iLoop= 0; aList[iLoop] >= 0; iLoop++) {
            printf( "Number:  %s\n", phonebook[ aList[iLoop] ]->number );
        }
    }
    else {
        printf( "That name is not in the phonebook or too many matches\n" );
    }
}

EDIT

You may also consider a slightly different approach to the find to create a find_name() that uses an iterator type of approach. The idea is to have a variable that acts as an iterator to the phone book. If there is a matching name then the iterator variable returns the pointers to the matching data.

Again I have not compiled or tested this so some work on your part may be required.

Since the iterator struct needs to be updated, we need to pass a pointer to the iterator struct so that it can be updated as we process the phonebook array.

int find_name( char *name, phonebookitera *pItera ) {
  int pos = -1;

  for (; pItera->pos < num_entries; pItera->pos++ ) {
    if ( strcmp( name, phonebook[ pItera->pos ]->name ) == 0 ) {
        // copy the matching pointers to our iterator 
        pItera->name = phonebook[ pItera->pos ]->name;
        pItera->number = phonebook[ pItera->pos ]->number;
        // we will return the matching position in phonebook though not necessary
        pos = pItera->pos;
        pItera->pos++;     // increment to the following element
        return pos;
    }
  }  

  return pos;
}

And this function would be used in something like the following code.

{
    phonebookitera myItera = {0};
    // iterate through the list to print out the matching entries.
    if (find_name (name, &myItera) >= 0) {
        do {
            printf( "Number:  %s\n", myItera.number );
        } while (find_name (name, &myItera) >= 0);
    } else {
        printf( "That name is not in the phonebook\n" );
    }

}

And you would define a phonebookitera as something like:

typedef struct {
    int  pos;
    char *name;
    char *number;
} phonebookitera;
Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • But how about in printing? I see after using that find_name, I will be able to get the number of items in the list, but how about the locations of the numbers to be printed? Sorry I'm not good in C, I'm not into it anymore. But thank you sir! – Glenn Posadas Oct 06 '14 at 14:54
0

The easiest way to get the list of numbers with NAMES as filter is this:

find_by_surname(surname); <--- inside the CASE FIND

and the find name function:

void find_name( char *name ) {

int i;
  if ( num_entries != 0 ) {
    printf( "Name\t\tNumber\n" );
    printf( "----\t\t------\n" );


    for ( i = 0; i < num_entries; i = i + 1 ) {
        if ( strcmp( name, phonebook[ i ]->name ) == 0 )
      printf( "%s\t\t%s\n", phonebook[ i ]->name, phonebook[ i ]->number );
    }
  }
  else {
    printf( "There are no entries in the phonebook\n" );
  }
}
Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
  • The problem with this solution is that you now have a side effect to your function which reduces reusing the function in other applications. The only way this could be reused is for a similar console based application where you just want to list the names in the phone book. If you wanted to do something else with the list of names, you could not use this function. For instance if you were developing a GUI where you wanted to populate a list box with a selection from the phone book, you would need to rewrite this function. – Richard Chambers Oct 22 '14 at 15:52