Same as 1. above ... but with functions added for edit, sort and file ...
/* phnBook1_withfile.c */ /* using a dynamic array to hold Contacts ... version 1 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> /* re. tolower ... */
#define FNAME "myContacts.txt" /* default file name ... */
#define MENU "\nContact Menu ...\n" \
"\t(1)\tAdd Contact\n" \
"\t(2)\tEdit/Delete Contact\n" \
"\t(3)\tShow All Contacts\n" \
"\t(4)\tsOrt All Contacts\n" \
"\t(5)\tFile All Contacts\n" \
"\t(6)\tLoad Contacts from file\n" \
"\t(7)\teXit ...\n" \
"Please enter a character from 1..7 or A,E,D,S,O,F,L,X : "
#define BUFSIZE 1024 /* pick an extra large size, to ensure? large enough ... */
const int BEGIN_SIZE = 1; /* BUT set BEGIN_SIZE to minimize calls to realloc */
int SIZE = 0; /* Global variable used to keep track of number of Contacts */
int CAP = 0; /* Global variable used to keep track of capacity for Contacts */
typedef struct myContact
{
char firstName[21]; /* NOTE! max num of char's for firstname is 20 */
char lastName[21]; /* NOTE! MUST LEAVE ROOM for terminal '\0' char */
char phoneNumber[11]; /* NOTE! max num of char's for phoneNumber is 10 */
} Contact ;
int fileContacts( const char*, Contact* );
Contact* loadContacts( const char*, Contact* );
Contact* addContact( Contact* );
void editDeleteContact( Contact*, char );
void printFormatted( const char* phone );
void showAllContacts( const Contact* );
int cmpNames( const Contact*, const Contact* );
void sortByNames( Contact* );
int cmpPhones( const Contact*, const Contact* );
void sortByPhones( Contact* );
int getChar( const char* msg );
void getFileName( char* filename );
int fileExists( const char* filename );
/* get a string from keyboard of length minSize to maxSize characters */
/* show 'msg' string and return right sized C string 'getstr' passed in */
void getString( const char* msg, char* getstr, int maxSize, int minSize );
/* get ALL line from file, and return all char's upto maxSize length in inStr */
/* 'safe fix to using fgets' to read a whole line less end '\n' char if exists */
/* Note: if input string exceeds maxSize-1 characters ... it gets truncated */
char* myFgets( char* inStr, int maxSize, FILE* fin );
int main( void ) /* ****************** BEGIN MAIN *************************** */
{
Contact* c = NULL;
char filename[BUFSIZE];
int reply = 'y';
printf( "Size of each record = sizeof(Contact) = %d bytes ...\n",
sizeof(Contact) );
while( !( reply == '7' || reply == 'x') )
{
printf( MENU ); fflush( stdout );
reply = getChar( "" );
if(reply == '1' || reply == 'a')
{
c = addContact( c ); /* update address of new memory */
if( c == NULL )
{
printf("\nError getting new memory in addContact ... "
"Press 'Enter' to exit ... "); fflush( stdout );
getchar();
return -1;
}
}
else if(reply == '2' || reply == 'e' || reply == 'd')
editDeleteContact( c, reply );
else if(reply == '3' || reply == 's')
showAllContacts( c );
else if(reply == '4' || reply == 'o')
{
int ans = getChar( "sOrt by names or phone (n/p) ? " );
if( ans == 'n' ) sortByNames( c );
else if( ans == 'p' ) sortByPhones( c );
else puts( "You didn't enter either 'n' or 'p' ..." );
}
else if(reply == '5' || reply == 'f')
{
getFileName( filename );
if( fileExists( filename ) )
{
int ans = getChar("Do you want to overwrite that file (y/n) ? ");
if( ans == 'y' )
{
int count = fileContacts( filename, c );
if( count == SIZE )
printf( "All %d records in the array were filed to",
count );
else
{
printf
(
"An error occured! "
"Only %d of %d records in the array were filed to",
count, SIZE
);
reply = 'x';
}
printf( " '%s'\n", filename );
}
else puts( "File write aborted ..." );
}
else
{
int count = fileContacts( filename, c );
if( count == SIZE )
printf( "All %d records in the array were filed to",
count);
else
{
printf
(
"An error occured! "
"Only %d of %d records in the array were filed to",
count, SIZE
);
reply = 'x';
}
printf( " '%s'\n", filename );
}
}
else if(reply == '6' || reply == 'l')
{
getFileName( filename );
if( fileExists(filename) )
{
int ans;
if( SIZE )
{
ans = getChar( "Do you want to append/overwrite "
"the array in memory (a/o) ? ");
if( ans == 'o' )
{
free( c );
SIZE = CAP = 0;
c = loadContacts( filename, c );
}
else if( ans == 'a' )
{
puts( "Ok ... will append to array ..." );
c = loadContacts( filename, c );
}
else puts( "You didn't enter either of 'A' or 'O' ... " );
}
else c = loadContacts( filename, c );
if( c && (ans == 'a' || ans == 'o') )
printf( "%d records are now in array memory.", SIZE );
else if( ans == 'a' || ans == 'o' )
{
puts( "There was an error allocating memory ..." );
reply = 'x';
}
}
else
{
printf( "The file '%s' does not exist yet ...\n", filename );
puts( "Select choice 'File All Contacts' "
"after you have added some contacts." );
}
}
else if(reply == '7' || reply == 'x' )
{
if( getChar("Do you really want to eXit (y/n) ? ") == 'y' )
{
if( getChar("\nWill eXit now ... "
"Press 'Enter' to continue/Enter 'a' to abort ... ")
== 'a' )
{
reply = 'y';
puts( "Exit aborted ..." );
}
else free( c );
}
else
{
reply = 'y';
puts( "Exit aborted ..." );
}
}
else if( reply != '\n' )
printf( "\nChoice '%c' NOT implemented yet ...\n", reply );
}
return 0;
} /* ***************************** END MAIN ********************************* */
int fileContacts( const char* filename, Contact* c )
{
FILE* fout = fopen( filename, "w" );
if( fout )
{
int i = 0;
for( i = 0; i < SIZE; ++i )
fprintf
(
fout,
"%s\n%s\n%s\n",
c[i].firstName, c[i].lastName, c[i].phoneNumber
);
fclose( fout );
return i;
}
/* else ... */
printf( "\nThere was an error trying to open file '%s' to write ...\n",
filename );
return 0;
}
Contact* loadContacts( const char* filename, Contact* c )
{
FILE* fin = fopen( filename, "r" );
if( fin )
{
int i = 0;
Contact tmp;
while
(
myFgets( tmp.firstName, sizeof(tmp.firstName), fin ) &&
myFgets( tmp.lastName, sizeof(tmp.lastName), fin ) &&
myFgets( tmp.phoneNumber, sizeof(tmp.phoneNumber), fin )
)
{
void* p;
if( SIZE == CAP ) /* if needed, reserve new memory to hold next Contact */
{
if( CAP != 0 ) CAP += CAP; /* double capacity ... */
else CAP = BEGIN_SIZE;
p = realloc( c, sizeof(Contact)*CAP );
if( p == NULL ) { free( c ); return NULL; }
else c = (Contact*) p; /* update with new address ... */
}
strcpy( c[SIZE].firstName, tmp.firstName );
strcpy( c[SIZE].lastName, tmp.lastName );
strcpy( c[SIZE].phoneNumber, tmp.phoneNumber );
++SIZE;
++i;
}
fclose( fin );
printf( "\n%d records were read from file %s ...\n", i, filename );
return c; /* return updated address ... */
}
/* else ... */
return NULL;
}
Contact* addContact( Contact* c )
{
void* p;
if( SIZE == CAP ) /* if needed, reserve new memory to hold next Contact */
{
if( CAP != 0 ) CAP += CAP; /* double capacity ... */
else CAP = BEGIN_SIZE;
p = realloc( c, sizeof(Contact)*CAP );
if( p == NULL ) { free( c ); return NULL; }
else c = (Contact*) p; /* update with new address ... */
}
getString( "First Name : ", c[SIZE].firstName, 20, 1 );
getString( "Last Name : ", c[SIZE].lastName, 20, 1 );
getString( "Phone Number : ", c[SIZE].phoneNumber, 10, 10 );
if( getChar("Ok to add (y/n) ? ") != 'y' )
{
puts( "Not added ..." );
return c;
}
printf("Contact successfully added ...\n");
++SIZE;
return c; /* return updated address ... */
}
void editDeleteContact( Contact* c, char reply )
{
int i = 0;
Contact ed;
if( reply == '2' ) reply = getChar("Edit or Delete (e/d) ? ");
puts( "Enter an 'empty' record to exit ..." );
getString( "First Name : ", ed.firstName, 20, 0 );
getString( "Last Name : ", ed.lastName, 20, 0 );
getString( "Phone Number : ", ed.phoneNumber, 10, 0 );
for( i = 0; i < SIZE; ++i )
{
if
(
strcmp(ed.firstName, c[i].firstName) == 0 &&
strcmp(ed.lastName, c[i].lastName) == 0 &&
strcmp(ed.phoneNumber, c[i].phoneNumber)==0
)
{
if( reply == 'd' &&
getChar("Do you really want to delete (y/n) ? ") == 'y' )
{
int j;
for( j = i; j < SIZE-1; ++j ) /* while more data ... */
{ /* copy down ... (1st one copied down is over deleted) */
strcpy(c[j].firstName, c[j+1].firstName);
strcpy(c[j].lastName, c[j+1].lastName);
strcpy(c[j].phoneNumber, c[j+1].phoneNumber);
}
printf("\nContact deleted ...\n");
--SIZE; /* update SIZE */
return; /* exit right now */
}
else if( reply == 'e' &&
getChar("Do you really want to edit (y/n) ? ") == 'y' )
{
getString( "First Name : ", ed.firstName, 20, 1 );
getString( "Last Name : ", ed.lastName, 20, 1 );
getString( "Phone number : ", ed.phoneNumber, 10, 10 );
if( getChar( "Ok to change (y/n) ? " ) == 'y' )
{
strcpy(c[i].firstName, ed.firstName);
strcpy(c[i].lastName, ed.lastName);
strcpy(c[i].phoneNumber, ed.phoneNumber);
return; /* exit right now */
}
else { puts("Aborted ..."); return; } /* exit right now */
}
else { puts("Aborted ..."); return; } /* exit right now */
}
}
/* else ... */
printf("Contact NOT found ...\n"); /* ... and exit void function */
}
void printFormatted( const char* phone )
{
char* p = "xxx-xxx-xxxx";
for( ; *p != 0; ++p )
{
if( *p == 'x' ) putchar( *phone++ );
else putchar( *p );
}
}
void showAllContacts( const Contact* c )
{
int i = 0;
printf( "\nContacts:\n" );
for( i = 0; i < SIZE; ++i )
{
printf("(%2d) ", i+1);
printFormatted( c[i].phoneNumber );
printf(" : %s, %s\n",c[i].lastName, c[i].firstName);
}
printf( "size = %d, capacity = %d\n", SIZE, CAP );
}
int cmpNames( const Contact* a, const Contact* b )
{
int last = strcmp( a->lastName, b->lastName );
if( last == 0 )
{
int first = strcmp( a->firstName, b->firstName );
if( first == 0 ) return strcmp( a->phoneNumber, b->phoneNumber );
else return first;
}
else return last;
}
int cmpPhones( const Contact* a, const Contact* b )
{
int phone = strcmp( a->phoneNumber, b->phoneNumber );
if( phone == 0 )
{
int last = strcmp( a->lastName, b->lastName );
if( last == 0 ) return strcmp( a->firstName, b->firstName );
else return last;
}
else return phone;
}
/* a simple example of a bubble sort is coded here ... */
void sortByNames( Contact* c )
{
int i, swap, size = SIZE; /* recall that SIZE is a global variable */
Contact tmp;
do
{
swap = 0;
for( i = 1; i < size; ++i )
{
if( cmpNames(&c[i-1], &c[i]) > 0 )
{
memcpy( &tmp, &c[i], sizeof(Contact) );
memcpy( &c[i], &c[i-1], sizeof(Contact) );
memcpy( &c[i-1], &tmp, sizeof(Contact) );
swap = 1;
/* // or swap like this ... //
tmp = c[i];
c[i] = c[i-1];
c[i-1] = tmp;
*/
/* // or swap like this ... //
strcpy(tmp.firstName, c[i].firstName);
strcpy(tmp.lastName, c[i].lastName);
strcpy(tmp.phoneNumber, c[i].phoneNumber);
strcpy(c[i].firstName, c[i-1].firstName);
strcpy(c[i].lastName, c[i-1].lastName);
strcpy(c[i].phoneNumber, c[i-1].phoneNumber);
strcpy(c[i-1].firstName, tmp.firstName);
strcpy(c[i-1].lastName, tmp.lastName);
strcpy(c[i-1].phoneNumber, tmp.phoneNumber);
*/
}
}
--size;
}while( swap );
printf( "Contacts sorted by last name, first name, phone number now ...\n" );
}
/* a simple example of a bubble sort is coded here ... */
void sortByPhones( Contact* c )
{
int i, swap, size = SIZE; /* recall that SIZE is a global variable */
Contact tmp;
do
{
swap = 0;
for( i = 1; i < size; ++i )
{
if( cmpPhones(&c[i-1], &c[i]) > 0 )
{
memcpy( &tmp, &c[i], sizeof(Contact) );
memcpy( &c[i], &c[i-1], sizeof(Contact) );
memcpy( &c[i-1], &tmp, sizeof(Contact) );
swap = 1;
}
}
--size;
}while( swap );
printf( "Contacts sorted by phone number, last name, first name now ...\n" );
}
int getChar( const char* msg )
{
int c, reply;
printf( "%s", msg ); fflush( stdout );
c = reply = getchar();
while( c != '\n' ) c = getchar(); /* 'flush' stdin ... */
return tolower( reply );
}
void getFileName( char* filename )
{
if( getChar("Use default file or input file name (d/i) ? ") == 'd' )
strcpy( filename, FNAME );
else
{
printf( "Input file name to use: " ); fflush( stdout );
myFgets( filename, BUFSIZE, stdin );
}
}
int fileExists( const char* filename )
{
FILE* f = fopen( filename, "r" );
if( f )
{
fclose( f );
return 1;
}
/* else ... */
return 0;
}
/* get a string from keyboard of length minSize to maxSize characters */
/* show 'msg' string and return right sized C string 'getstr' passed in */
void getString( const char* msg, char* getstr, int maxSize, int minSize )
{
char *p, buffer[BUFSIZE];
int len;
for( ; ; ) /* loop forever ... until break ... */
{
printf( msg );
fflush( stdout );
fgets( buffer, BUFSIZE, stdin );
p = strchr( buffer, '\n' );
if( p != NULL ) *p = 0; /* eliminate '\n' char at end of C string */
len = strlen(buffer);
if( len <= maxSize && len >= minSize ) break;
/* else... */
printf( "\nERROR! Length was %d ... Valid length is in range %d..%d\n",
len, minSize, maxSize );
}
/* when reach here ... a good len was input ... */
strcpy(getstr, buffer); /* getstr is retuned 'by ref' ... */
}
/* get ALL line from file, and return all char's upto maxSize length in inStr */
/* 'safe fix to using fgets' to read a whole line less end '\n' char if exists */
/* Note: if input string exceeds maxSize-1 characters ... it gets truncated */
char* myFgets( char* inStr, int maxSize, FILE* fin )
{
char myBuf[BUFSIZE] = { 0 }; /* fill with '\0' char's ... */
if( fgets(myBuf, BUFSIZE, fin) ) /* i.e. fgets(myBuf, BUFSIZE, fin) != NULL */
{
char* p = strchr( myBuf, '\n' );
if( p ) *p = 0; /* eliminate '\n' char at end of C string, if exists */
else /* 'flush' any remaining char's in this extra long input string */
{
int c = fgetc(fin);
while( c != '\n' && c != EOF ) c = fgetc(fin);
}
strncpy(inStr, myBuf, maxSize); /* now only copy first maxSize char's */
if( inStr[maxSize-1] != 0 ) inStr[maxSize-1] = 0; /* inStr is truncated */
return inStr;
}
else return NULL;
}