4. Finally, here we use readLine (instead of fgets with a large buffer) ... and also we use xxx.h files ... readLine.h, Cvec.h and Cvec_func's.h ... to facilitate coding and code re-use.
Note: you will need all 3 of these following linked files, to be available to include, before you compile this 4th demo program in this series ...
readLine.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2864.html#msg2864Cvec.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2862.html#msg2862Cvec_func's.h
http://developers-heaven.net/forum/index.php/topic,2580.msg2866.html#msg2866/* phnBook4.c */ /* 2016-10-12 */
/* using readLine.h and Cvec.h and Cvec_func's.h ... */
#include "readLine.h" /* includes <stdio.h>, <stdlib.h>, <string.h>, etc... */
#define MENU "Contact Menu ...\n" \
"\t\t\t(1)\tAdd Contact\n" \
"\t\t\t(2)\tDelete Contact\n" \
"\t\t\t(3)\tShow All Contacts\n" \
"\t\t\t(4)\tsOrt All Contacts\n" \
"\t\t\t(5)\teXit ...\n" \
"Enter your choice 1..5 > "
#define VEC_CHUNK_SIZE 2 /* if not defined here, defaults to initial cap = 8 */
/* first ... define Rec ... */
typedef struct myContact
{
char* firstName;
char* lastName;
char* phoneNumber; /* num of char's for phoneNumber is 10 + terminal 0 */
} Rec ;
/* and then define void freeVrec( Rec* ); ... */
void clearRec( Rec* rc )
{
free( rc->phoneNumber );
free( rc->lastName );
free( rc->firstName );
}
/* now can ... */
#include "Cvec.h"
#include "Cvec_func's.h"
/* you also need to define 'equals'... via a 'your_cmp_equals' 2 Rec's function */
int cmp_equals( const Rec* a, const Rec* b ) /* for findCvec( .. ) to work */
{
if( strcmp(a->firstName, b->firstName) == 0
&& strcmp(a->lastName, b->lastName) == 0 ) return 0; /* def'n equals */
else return 1;
}
int my_sort_cmp( const Rec* a, const Rec* b ) /* for msortCvec( .. ) to work */
{
int eq = strcmp(a->lastName, b->lastName);
if( eq == 0 ) return strcmp(a->firstName, b->firstName);
/* else ...*/
return eq;
}
void addContact( Cvec* );
void deleteContact( Cvec* );
void showAllContacts( Cvec* );
char* takeInLine( const char* msg )
{
printf( msg );
return readLine( stdin );
}
char* takeInLineMinMax( const char* msg, int min, int max )
{
char* line;
int len;
for( ; ; ) /* loop forever ... until break ... */
{
line = takeInLine( msg ); /* using readLine.h included above ... */
len = strlen(line);
if( min <= len && len <= max )
return line;
/* else... */
printf( "\nERROR! Input must have length in range %d..%d\n", min, max );
free( line );
}
}
char* takeInLineSize( const char* msg, int len )
{
char* line;
for( ; ; ) /* loop forever ... until break ... */
{
line = takeInLine( msg ); /* using readLine.h included above ... */
if( (int)strlen(line) == len ) return line;
/* else... */
printf( "\nERROR! Input must have length %d\n", len );
free( line );
}
}
int isValidPhoneNum( const char* s )
{
int i = strlen( s ) - 1;
for( ; i >= 0; --i ) if( !isdigit( s[i] ) ) return 0;
/* else ... if reach here .. */
return 1;
}
/* 's' is the C string passed in ... and WAS validated as 10 digits long */
void printFormattedPhoneNum( const char* s )
{
const char* p = "xxx-xxx-xxxx";
/* printf( "Number: " ); */
for( ; *p != 0 ; ++p )
{
if( *p == 'x' )
{
putchar( *s );
++s;
}
else putchar( *p );
}
/* putchar( '\n' ); */
}
void printRec( Rec* rc )
{
printFormattedPhoneNum( rc->phoneNumber );
printf( ": %s %s", rc->firstName, rc->lastName );
}
int main( void )
{
int reply;
Cvec phonebook;
initCvec( &phonebook );
printf( "Size of each record = sizeof(Rec) = %d bytes ...\n", (int)sizeof(Rec) );
do
{
reply = toupper(takeInChar( MENU ));
if( reply == '1' || reply == 'A' )
addContact( &phonebook ); /* Cvec updated since address passed */
else if( reply == '2' || reply == 'D' )
deleteContact( &phonebook ); /* Cvec updated ... */
else if( reply == '3' || reply == 'S' )
showAllContacts( &phonebook ); /* no copy made ... address passed */
else if( reply == '4' || reply == 'O' )
{ puts( "Now sorted ..." ); msortCvec( &phonebook, my_sort_cmp ); }
else if( reply == '5' || reply == 'X' )
{
clearCvec( &phonebook ); /* Cvec updated ... */
printf( "\nWill exit now ... Press 'Enter' to continue ... " );
fflush( stdout );
getchar();
}
else if( reply == '\n' ) printf( "\nYou need to enter something ...\n" );
else printf( "\nChoice '%c' NOT implemented here ...\n", reply );
}
while( !(reply == '5' || reply == 'X') );
return 0;
} /* end of main ... */
void addContact( Cvec* phonebook )
{
do
{
Rec r;
r.firstName = takeInLineMinMax( "First Name : ", 1, 80 ); /* pointer copied */
r.lastName = takeInLineMinMax( "Last Name : ", 1, 80 );
strToTitleCase(r.firstName);
strToTitleCase(r.lastName);
for( ; ; )
{
r.phoneNumber = takeInLineSize( "Phone Number : ", 10 );
if( isValidPhoneNum( r.phoneNumber ) ) break;
/* else ... */
printf( "\n%s is not all digits ...\n", r.phoneNumber );
free( r.phoneNumber );
}
if( tolower(takeInChar( "Ok (y/n) ? ")) == 'y' )
{
push_backCvec( phonebook, &r ); /* copies Rec pointers into array memory */
printRec( &r );
printf( " was successfully added ...\n" );
}
else
{
printRec( &r );
printf( " NOT added ...\n" );
clearRec( &r );
}
}
while( more() ) ;
}
void deleteContact( Cvec* phonebook )
{
int i;
Rec r;
r.firstName = takeInLineMinMax( "First Name : ", 0, 100 ); /* pointer copied */
r.lastName = takeInLineMinMax( "Last Name : ", 0, 100);
i = findCvec( phonebook, &r, cmp_equals ); /* uses def'n 'cmp_equals function' */
if( i != -1 ) /* findCvec returns index if found ... or -1 if not found */
{
eraseCvec( phonebook, i ); /* 'phonebook' is already an address */
printf( "Contact deleted from Cvec ...\n" );
}
else printf("Contact NOT found ...\n");
clearRec( &r ); /* free memory of dynamic C strings in this Rec 'r' ... */
}
void showAllContacts( Cvec* phonebook )
{
int x = 0;
printf( "\nContacts:" );
for( x = 0; x < phonebook->size; ++x )
{
printf( "\n(%d)\n", x+1);
printf( "Name: %s %s\n",
phonebook->ary[x].firstName, phonebook->ary[x].lastName );
/* printf( "Number: %s\n", phonebook->ary[x].phoneNumber ); */
printf( "Number: " );
printFormattedPhoneNum( phonebook->ary[x].phoneNumber );
}
printf( "\nsize = %d, capacity = %d\n", phonebook->size, phonebook->cap );
}