4 Replies - 2066 Views - Last Post: 22 April 2012 - 04:54 PM Rate Topic: -----

#1 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

C vector emulation

Posted 05 March 2010 - 11:51 PM

Description: See program and comments

Note: a copy of the readLine.h file, that includes the readLine function and also the myAssert function ... is included at the bottom here.An emulation of the C++ STL vector, (just a start here.)

This program demo's using push_back of employee records ... (Handling employee records is a common C student programming need ... and a C++ like vector push_back function in C ... might be a big help.)
/* employeeRec's_4qsort.c */

/* C 'vector emulation' using a dynamic array of rec's (see demo MENU below) */

#include <ctype.h> /* re. tolower */

/* Also ... using readLine instead of gets and fgets */
#include "readLine.h" /* includes stdio.h, stdlib.h  ... also myAssert( ... ) */

#define FILE_NAME "employeeRecords.txt"
#define MENU      " 1. Enter a new record. n" 
                  " 2. Retrieve a record by name. n" 
                  " 3. Retrieve a record by phone number. n" 
                  " 4. Show all records. n" 
                  " 5. QSort all records by name. n" 
                  " 6. QSort all records by phone number. n" 
                  " 7. Chop exact duplicates from records. n" 
                  " 8. Edit/Delete a record. n" 
                  " 9. Write new file of sorted unique records. n" 
                  "10. Exit n"
#define PROMPT    "Enter a number in range 1 to 10 : "


/* Cvect.h */

#ifndef _CVECTOR_
#define _CVECTOR_

/* Note: stdio.h, stdlib.h and myAssert were included in "readLine.h" above */
#include <string.h> /* re. memcpy */

#define REC_CHUNK_SIZE 5 /* re-set/increase this to best match your data size */

typedef struct employeeRecord
{
    char* name;     /* since CStrings are '�' terminated ... can get strlen */
    char* address;  /* ditto above ... */
    char* phone;    /* 3 digit area-code  +  7 digits  +  '�'  at end */
} Dat ;

typedef struct myCvec
{
    Dat* ary;
    int size;
    int cap; /* capacity*/
} Cvec;

/* with these, an address is passed, so NO copy made and/or original updated */
void push_back( Cvec* ad, Dat* d );
void resize( Cvec* ad );
void freeAll( Cvec* ad );

void push_back( Cvec* ad, Dat* d )
{
    if( ad->size == ad->cap ) resize( ad );
    /* now add in new dat ... */
    memcpy( &(ad->ary[ad->size]), d, sizeof(Dat) );
    /*
    ad->ary[ad->size].name = d->name;
    ad->ary[ad->size].address = d->address;
    ad->ary[ad->size].phone = d->phone;
    */
    ++ ad->size;
}

/* get new array to hold one more record than before, copies old to new ...*/
void resize( Cvec* ad )
{
    Dat* p;
    /* int i; */
    ad->cap += REC_CHUNK_SIZE;
    p = calloc( ad->cap, sizeof(Dat) );
    if( p == NULL )
    {
        freeAll( ad );
        myAssert( 0, "Error: calloc failed to allocate memory." );
    }
    /* else ... */
    memcpy( &p[0], &(ad->ary[0]), sizeof(Dat)*(ad->size) );
    /*
    for( i = 0; i < ad->size; ++i )
    {
        p[i].name = ad->ary[i].name; // copy pointers to new array of pointers
        p[i].address = ad->ary[i].address;
        p[i].phone = ad->ary[i].phone;
    }
    */
    if( ad->ary != NULL )
        free( ad->ary ); /* free memory of old array that held pointers ...*/

    ad->ary = p; /* update ... the base address of our ad->ary ... */
}

void freeAll( Cvec* ad )
{
    int i;
    for( i = ad->size-1; i >= 0; --i )
    {
        free( ad->ary[i].phone );
        free( ad->ary[i].address );
        free( ad->ary[i].name );
    }
    if( ad->ary != NULL ) free( ad->ary );
    ad->ary = NULL;
    ad->size =ad->cap = 0; /* update ... */
}

#endif


/* with these, an address is passed, so NO copy made and/or original updated */
void readFile( Cvec* ad );
int showMenuGetChoice( Cvec* ad );
int isValidPhoneNum( char ph[] );
int compareIgnoreCase( char s1[], char s2[] );
int getNameIgnoreCase( Cvec* ad, char nameStr[] );
int getNumber( Cvec* ad, char numStr[] );
void show( Cvec* ad, int i ); /* show rec. at index i */
void showAll( Cvec* ad );
void takeInDatAndFile( Cvec* ad );
void qsortAllNames( Cvec* ad );
void qsortAllPhones( Cvec* ad );
int compareERecsNameIgnoreCase( const void* x, const void* y );
int compareERecsPhone( const void* x, const void* y );
void chopDups( Cvec* ad );
int compareDat( Dat* x, Dat* y );
void writeSortedUnique( Cvec* ad );
void editDel( Cvec * ad );
void edit( Cvec* ad, int i ); /* at index i ... */
void del( Cvec* ad, int i );



int main() /* ********************* MAIN BEGINS ***************************** */
{
    int choice;
    Cvec cv;
    cv.ary = NULL;
    cv.size = cv.cap = 0;
    readFile( &cv ); /* we are passing the address of cv, so cv is updated */
    if( cv.size == 0 )
        printf
        (
            "File %s will be created after data has been entered.n",
            FILE_NAME
        );
    else showAll( &cv ); /* passing address to avoid making another copy */

    printf( "Now cv.ary = 0x%p ... cv.size = %dnn", cv.ary, cv.size );

    do
    {
        choice = showMenuGetChoice( &cv ); /* passing address ... */
    }
    while( choice != 10 ); /* i.e. exit on choice of 9 ...  */

    freeAll( &cv ); /* free all dynamic memory when done with it ... */

    printf( "nNow cv.ary = 0x%p ... cv.size = %dn", cv.ary, cv.size );

    /* if using windows ... can do this ... especially while debugging ... */
    system( "notepad " FILE_NAME );
    return 0;
} /* **************************** MAIN ENDS ********************************* */



void readFile( Cvec* ad )
{
    FILE* fp;
    if( !(fp = fopen(FILE_NAME, "r")) )
    {
        printf( "File %s does not exist yet in this folder ...n", FILE_NAME );
    }
    else
    {
        Dat d;
        while
        (
            (d.name = readLine(fp)) &&
            (d.address = readLine(fp)) &&
            (d.phone = readLine(fp))
        )
        {
            push_back( ad, &d );
        }
        fclose( fp );
    }
}

int showMenuGetChoice( Cvec* ad )
{
    int num;
    char* tmp = NULL;

    fputs( MENU, stdout );
    while
    (
        printf( PROMPT ) &&
        (tmp = readLine(stdin)) != NULL &&
        ( (num = atoi(tmp)) < 1 || num > 10 )
    )
    {
        fputs( "Out of valid range 1..10  ", stdout );
        free(tmp);
    }
    free(tmp);

    if( num == 1 )
    {
        takeInDatAndFile( ad );
    }
    else if( num == 2 )
    {
        int index;
        fputs( "Enter the name to find: ", stdout );
        tmp = readLine(stdin);
        if( (index = getNameIgnoreCase( ad, tmp )) == -1 )
            printf( "%s not found.n", tmp );
        else
            show( ad, index );
        free( tmp );
    }
    else if( num == 3 )
    {
        int index;
        fputs( "Enter the number to find: ", stdout );
        tmp = readLine(stdin);
        if( (index = getNumber( ad, tmp )) == -1 )
            printf( "%s not found.n", tmp );
        else
            show( ad, index );
        free( tmp );
    }
    else if( num == 4 ) showAll( ad );
    else if( num == 5 ) qsortAllNames( ad );
    else if( num == 6 ) qsortAllPhones( ad );
    else if( num == 7 ) chopDups( ad );
    else if( num == 8 ) editDel ( ad );
    else if( num == 9 ) writeSortedUnique( ad );
    /* else is 10 ... so will exit ... */

    return num;
}

/* validates that 10 char's are present and all are numeric ... */
int isValidPhoneNum( char ph[] )
{
    if( strlen(ph) != 10 ) return 0;
    for( ; *ph != 0; ++ph )
        if( *ph < '0' || *ph >'9' ) return 0;
    return 1;
}

/* ignore case compare function ... */
int compareIgnoreCase( char s1[], char s2[] )
{
    for( ; *s1 != 0 && *s2 != 0; ++s1, ++s2 )
    {
        if( tolower(*s1) != tolower(*s2) ) break;
    }
    /* ok ... check the end conditions ... */
    if( *s1 == 0  &&  *s2 == 0 ) return 0; /*  strings equal ...*/
    return tolower(*s1) - tolower(*s2);
}

/* returns index ... if found ... otherwise , returns -1 if NOT found */
int getNameIgnoreCase( Cvec* ad, char nameStr[] )
{
    int i;
    for( i = 0; i < ad->size; ++i)
        if( compareIgnoreCase(ad->ary[i].name, nameStr) == 0 )
            return i;
    return -1;
}

int getNumber( Cvec* ad, char numStr[] )
{
    int i;
    for( i = 0; i < ad->size; ++i)
        if( strcmp(ad->ary[i].phone, numStr) == 0 )
            return i;
    return -1;

}

void show( Cvec* ad, int i )
{
    printf
    (
        "<%03d> Name: %s,  Address: %s,  Phone: %sn",
        i+1, ad->ary[i].name, ad->ary[i].address, ad->ary[i].phone
    );
}
void showAll( Cvec* ad )
{
    int i;
    for( i = 0; i < ad->size; ++i ) show( ad, i );
    printf( "Employee records on file = %d :: Max capacity = %dnn",
            ad->size, ad->cap );
}

void takeInDatAndFile( Cvec* ad )
{
    Dat d;
    FILE* pFile = fopen( FILE_NAME, "a" );
    myAssert( (pFile != NULL), "Error: " FILE_NAME " not opened." );

    printf( "Enter name: " );
    d.name = readLine( stdin );
    fprintf( pFile, "%sn",  d.name );

    printf("Enter address: ");
    d.address = readLine( stdin );
    fprintf(pFile, "%sn", d.address);

    for( ; ; )
    {
        printf( "Enter telephone: " );
        d.phone = readLine( stdin );
        if( isValidPhoneNum(d.phone) )
        {
            fprintf( pFile, "%sn", d.phone );
            break;
        }
        /* else ... */
        fputs( "Only 10 digit number valid here ... ", stdout );
    }

    fclose( pFile );
    push_back( ad, &d );
    printf( "Information has been filed in file %s.n", FILE_NAME );
}

void qsortAllNames( Cvec* ad )
{
    qsort( ad->ary, ad->size, sizeof(Dat), compareERecsNameIgnoreCase );
}
void qsortAllPhones( Cvec* ad )
{
    qsort( ad->ary, ad->size, sizeof(Dat), compareERecsPhone );
}

int compareERecsNameIgnoreCase( const void* x, const void* y )
{
    Dat* c1 = (Dat*) x;
    Dat* c2 = (Dat*) y;
    /* return strcmp(c1->name, c2->name); //or, this line only if need case// */
    int i = 0;
    while( c1->name[i] != 0 && c2->name[i] != 0 )
    {
        if( tolower(c1->name[i]) != tolower(c2->name[i]) )
            break;
        ++i;
    }
    /* now test end conditions .... */
    if( c1->name[i] == 0  &&   c2->name[i] == 0 ) /* i.e. if names the same */
    {
        return strcmp( c1->phone, c2->phone ); /* then use phone numbers ... */
    }
    /* else ... */
    return tolower(c1->name[i]) - tolower(c2->name[i]); /* else, use next char */
}

int compareERecsPhone( const void* x, const void* y )
{
    Dat* c1 = (Dat*) x;
    Dat* c2 = (Dat*) y;
    int compare = strcmp( c1->phone, c2->phone );
    if( compare == 0 ) /* if numbers the same ... */
        return strcmp( c1->name, c2->name ); /* then use names ... */
    /* else ... */
    return compare; /* use numbers ... since number's were NOT the same ...*/
}

void chopDups( Cvec* ad )
{
    int i, bot, top = ad->size-1;

    qsortAllNames( ad ); /* first make sure in name-sorted order ... */
    for( bot = 0; bot < top; ++bot )
    {
        i = bot;
        while( i < top && compareDat( &(ad->ary[bot]), &(ad->ary[i+1]) ) == 0 )
        {
            ++i; /* increment i while next rec. up stream is a duplicate ... */
        }
        /* if duplicate(s) ... copy last duplicate rec, upstream, down-over(bot)
           (and all following) ... and reset top, bot(at top of for loop) */
        if(i-bot)
        {
            memcpy( &(ad->ary[bot]), &(ad->ary[i]), (top-i+1)*sizeof(Dat) );
            top -= (i-bot);
        }
    }
    /* finally ... re-set size to new (top+1) of unique number of rec's ... */
    /* and ... leave capacity unchanged ... (for now) ...  */
    ad->size = top+1;
}

int compareDat( Dat* x, Dat* y )
{
    int compare1 = strcmp( x->name, y->name ); /* first compare names ...*/
    if( compare1 == 0 ) /* if the same ... */
    {
        int compare2 = strcmp( x->address, y->address ); /* compare addresses */
        if( compare2 == 0 ) /* if the same ... */
            return strcmp( x->phone, y->phone ); /* use phone number then ... */
        /* else ...*/
        return compare2; /* use addresses after all  ... */
    }
    /* else ... */
    return compare1; /* use names ... since names were NOT ythe same ... */
}

void writeSortedUnique( Cvec* ad )
{
    int i;
    FILE* fp = fopen( "SU" FILE_NAME, "w" ); /* compiler concat's these TEXTs */
    myAssert( (fp != NULL), "Error: file SU" FILE_NAME " failed to open." );

    /* ok ... file is open ... so write all rec's to file ..*/

    chopDups( ad ); /* But first ... just to make sure sorted and unique ... */
    for( i = 0; i < ad->size; ++i )
    {
        fprintf( fp, "%sn", ad->ary[i].name );
        fprintf( fp, "%sn", ad->ary[i].address );
        fprintf( fp, "%sn", ad->ary[i].phone );
    }
    fclose( fp ); /* flushes buffer ... so all written to file now ... */
    printf( "n%d records filed in file SU%snn", i, FILE_NAME );

    /* and if have Windows OS ... can do this ... */
    system( "notepad SU" FILE_NAME ); /*compiler concat's text at compile time*/
}

void editDel( Cvec * ad )
{
    int c, reply, index;
    char* tmp;
    for( ; ; )
    {
        fputs( "Find by Name/Phone/Abort (n/p/a) ? ", stdout );
        c = reply = tolower(getchar());
        while( c != 'n' ) c = getchar(); /* flush stdin ... */
        if( reply != 'n' && reply != 'p' && reply != 'a' ) continue;
        else if( reply == 'n' )
        {
            fputs( "Enter the name to find: ", stdout );
            tmp = readLine(stdin);
            if( (index = getNameIgnoreCase( ad, tmp )) == -1 )
                printf( "%s not found.n", tmp );
            else
            {
                show( ad, index );
                fputs( "Edit/Delete/Abort (e/d/a) ? ", stdout );
                c = reply = tolower(getchar());
                while( c != 'n' ) c = getchar(); /* flush stdin ... */
                if( reply != 'e' && reply != 'd' && reply != 'a' ) continue;
                else if( reply == 'e' ) edit( ad, index );
                else if( reply == 'd' ) del( ad, index );
                else
                {
                    puts( "Ok, edit/delete aborted ..." );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'p' )
        {
            fputs( "Enter the number to find: ", stdout );
            tmp = readLine(stdin);
            if( (index = getNumber( ad, tmp )) == -1 )
                printf( "%s not found.n", tmp );
            else
            {
                show( ad, index );
                fputs( "Edit/Delete/Abort (e/d/a) ? ", stdout );
                c = reply = tolower(getchar());
                while( c != 'n' ) c = getchar(); /* flush stdin ... */
                if( reply != 'e' && reply != 'd' && reply != 'a' ) continue;
                else if( reply == 'e' ) edit( ad, index );
                else if( reply == 'd' ) del( ad, index );
                else
                {
                    puts( "Ok, edit/delete aborted ..." );
                    break;
                }
            }
            free( tmp );
        }
        else
        {
            puts( "Ok, edit/delete aborted ..." );
            break;
        }
    }
}

void edit( Cvec* ad, int i )
{
    Dat d;
    int c, reply;
    for( ; ; )
    {
        printf( "Enter name: " );
        d.name = readLine( stdin );
        printf("Enter address: ");
        d.address = readLine( stdin );
        for( ; ; )
        {
            printf( "Enter telephone: " );
            d.phone = readLine( stdin );
            if( isValidPhoneNum(d.phone) ) break; /* out of INNER for loop ...*/
            /* else ... */
            fputs( "Only 10 digit number valid here ... ", stdout );
            free( d.phone );
        }
        /* ok ... we have some new data ... */
        fputs( "Ok ... (y/n) ? ", stdout );
        c = reply = tolower( getchar() );
        while( c != 'n' ) c = getchar(); /* flush stdin ... */
        if( reply == 'y' )
        {
            /* then edit ... but first free old ...  */
            free( ad->ary[i].name );
            free( ad->ary[i].address );
            free( ad->ary[i].phone );

            ad->ary[i].name =d.name;
            ad->ary[i].address =d.address;
            ad->ary[i].phone =d.phone;
            break;
        }
        else
        {
            puts( "Aborted ..." );
            free( d.name );
            free( d.address );
            free( d.phone );
            break;
        }
    }
}

void del( Cvec* ad, int i )
{
    int c, reply;
    fputs( "Really delete/abort (d/a) ? ", stdout );
    for( ; ; )
    {
        c = reply = tolower(getchar());
        while( c != 'n' ) c = getchar(); /* flush stdin ... */
        if( reply == 'a' )
        {
            puts( "Delete aborted ..." );
            break;
        }
        else if( reply == 'd' )
        {
            if( i < ad->size-1 )
                memcpy( &(ad->ary[i]), &(ad->ary[i+1]), (ad->size-1-i)*sizeof(Dat) );
            -- ad->size;
            break;
        }
        /* else ... */
        fputs( "Enter 'd' and WILL delete ... or 'a' to abort: ", stdout );
    }
}




/* readLine.h */

#ifndef _READ_LINE_
#define _READ_LINE_

/* can re-set/adjust here to best match your line-length */
#define LINE_CHUNK 256

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

#include <stdio.h>
#include <stdlib.h>

/*
    Safe string data entry from file ... to replace gets and fgets
    BUT free new memory ... when done! Don't forget that C strings are
    pointers to a block of char, with a '�' char at the terminal end ...

    Call like this:

        char* line = readLine( stdin );
    
        or ...
    
        while( (line = readLine( FILEp )) )
        {
            // process line ...
        }
*/

void myAssert( int condition, char message[] );

/* returns a string in new memory holding each word ... or NULL if EOF */
char* readLine( FILE* fp  )
{
    int c, i = 0; /* c to hold each char, i for index in string ... */
    int LINE_CHUNK_len = LINE_CHUNK;
    void* tmp;
    char* strData = (char*) calloc( LINE_CHUNK_len, 1 ); /* cast for C++ compilers */
    myAssert( (strData != NULL), "Error: calloc failed to allocate memory (1)" );
    while( (c = fgetc(fp)) != EOF  &&  c != 'n' )
    {
        if( i == LINE_CHUNK_len-2 ) /* get more memory now (for word or line) */
        {
            LINE_CHUNK_len += LINE_CHUNK; /* enlarge memory */
            if( (tmp = realloc(strData, LINE_CHUNK_len)) == NULL )
            {
                free(strData);
                myAssert( 0, "Error: realloc failed to allocate memory (2)" );
            }
            strData  = (char*) tmp; /* good realloc above, so update pointer */
        }   
        strData[i] = c;
        ++i;
    }
    strData[i] = 0; /* confirm termination */

    if( c == EOF  && strData[0] == 0 ) { free( strData); return NULL; }
    /* else ... since didn't return NULL above, adjust string to right-size */
    if( (tmp = realloc(strData, i+1)) == NULL )
    {
        free(strData);
        myAssert( 0, "Error: realloc failed to allocate memory (3)" );
    }
    return (char*) tmp; /* return the right-sized string ... */
}

void myAssert( int condition, char message[] )
{
    if( !condition )
    {
        fputs( message, stderr );
        fputs( "nPress 'Enter' to exit ... ", stderr );
        getchar();
        exit(1);
    }
}

#endif



Is This A Good Question/Topic? 0
  • +

Replies To: C vector emulation

#2 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

Re: C vector emulation

Posted 05 March 2010 - 11:51 PM

Description: See program and comments

Note: a copy of the readLine.h file, that includes the readLine function and also the myAssert function is included at the bottom here.An emulation of the C++ STL vector ... (just a start here)

This program demo's using push_back of employee records (Handling employee records is a common C student programming need and a C++ like vector push_back function in C might be helpful.)
/* employeeRec's_4qsortEditDelete2.c */

/* C 'vector emulation' using a dynamic array of rec's (see demo MENU below) */

#include <ctype.h> /* re. tolower */

#define FILENAME "employeeRecords.txt"
#define MENU      " 1. Enter a new record. n" 
                  " 2. Retrieve a record by name. n" 
                  " 3. Retrieve a record by phone number. n" 
                  " 4. Show all records. n" 
                  " 5. QSort all records by name. n" 
                  " 6. QSort all records by phone number. n" 
                  " 7. Chop exact duplicates from records. n" 
                  " 8. Edit/Delete a record. n" 
                  " 9. Write new file of sorted unique records. n" 
                  "10. Exit n"
#define PROMPT    "Enter a number in range 1 to 10 : "


/* Cvect.h */

#ifndef dwCVECTOR
#define dwCVECTOR

/* using readLine here ... instead of gets and fgets */
#include "readLine.h" /* includes stdio.h, stdlib.h  ... also myAssert( ... ) */

/* Note: stdio.h, stdlib.h and myAssert were included in "readLine.h" above */
#include <string.h> /* re. memcpy */

#define REC_CHUNK_SIZE 5 /* re-set/increase this to best match your data size */

typedef struct employeeRecord
{
    char* name;     /* since CStrings are '�' terminated ... can get strlen */
    char* address;  /* ditto above ... */
    char* phone;    /* 3 digit area-code  +  7 digits  +  '�'  at end */
} Dat ;

typedef struct myCvec
{
    Dat* ary;
    int size;
    int cap; /* capacity*/
    int isSorted;
} Cvec;

/* with these, an address is passed, so NO copy made and/or original updated */
void init( Cvec* ); /* sets ary to NULL and size, cap to 0 and isSorted to 1 */
void push_back( Cvec* ad, Dat* d );
void resize( Cvec* ad );
void freeAll( Cvec* ad );

void init( Cvec* cv )
{
    cv->ary = NULL;
    cv->size = cv->cap = 0;
    cv->isSorted = 1;
}

void push_back( Cvec* ad, Dat* d )
{
    if( ad->size == ad->cap ) resize( ad );
    /* now add in new dat ... */
    memcpy( &(ad->ary[ad->size]), d, sizeof(Dat) );
    /*
    ad->ary[ad->size].name = d->name;
    ad->ary[ad->size].address = d->address;
    ad->ary[ad->size].phone = d->phone;
    */
    ++ ad->size;
    if( ad->size > 1 ) ad->isSorted = 0;
}

/* get new array to hold one more record than before, copies old to new ...*/
void resize( Cvec* ad )
{
    Dat* p;
    ad->cap += REC_CHUNK_SIZE;
    p = realloc(ad->ary, ad->cap * sizeof(Dat) );
    if( p == NULL )
    {
        freeAll( ad );
        myAssert( 0, "Error: realloc failed to allocate memory." );
    }
    /* else ... */
    ad->ary = p; /* update the base address of ad->ary */
}

void freeAll( Cvec* ad )
{
    if( ad->ary != NULL )
    {
        int i;
        for( i = ad->size-1; i >= 0; --i )
        {
            free( ad->ary[i].phone );
            free( ad->ary[i].address );
            free( ad->ary[i].name );
        }
        free( ad->ary );
        init( ad );
    }
}

#endif


/* with these, an address is passed, so NO copy made and/or original updated */
void readFile( Cvec* ad );
int showMenuGetChoice( Cvec* ad );
int isValidPhoneNum( char ph[] );
int compareIgnoreCase( char s1[], char s2[] );
int getNameIgnoreCase( Cvec* ad, char nameStr[] );
int getNumber( Cvec* ad, char numStr[] );
void show( Cvec* ad, int i ); /* show rec. at index i */
void showAll( Cvec* ad );
void takeInDatAndFile( Cvec* ad );
void qsortNames( Cvec* ad );
void qsortPhones( Cvec* ad );
int compareNameIgnoreCase( const void* x, const void* y );
int comparePhone( const void* x, const void* y );
void chopDups( Cvec* ad );
int compareDat( Dat* x, Dat* y );
void writeSortedUnique( Cvec* ad );
void editDel( Cvec* ad );
void edit( Cvec* ad, int i ); /* at index i ... */
void del( Cvec* ad, int i );



int main() /* ********************* MAIN BEGINS ***************************** */
{
    int choice;
    Cvec cv;
    init(&cv);
    readFile( &cv ); /* we are passing the address of cv, so cv is updated */
    if( cv.size == 0 )
        printf
        (
            "File %s will be created after data has been entered.n",
            FILENAME
        );
    else showAll( &cv ); /* passing address to avoid making another copy */
    
    printf( "Now cv.ary = 0x%p ... cv.size = %dnn", cv.ary, cv.size );
    
    do
    {
        choice = showMenuGetChoice( &cv ); /* passing address ... */
    }
    while( choice != 10 ); /* i.e. exit on choice of 10 ...  */
    
    freeAll( &cv ); /* free all dynamic memory when done with it ... */
    
    printf( "nNow cv.ary = 0x%p ... cv.size = %dn", cv.ary, cv.size );

    /* if using windows ... can do this ... especially while debugging ... */
    system( "notepad " FILENAME );
    return 0;
} /* **************************** MAIN ENDS ********************************* */



void readFile( Cvec* ad )
{
    FILE* fp;
    if( !(fp = fopen(FILENAME, "r")) )
    {
        printf( "File %s NOT found ...n", FILENAME );
    }
    else
    {
        Dat d;
        while
        (
            (d.name = readLine(fp)) &&
            (d.address = readLine(fp)) &&
            (d.phone = readLine(fp))
        )
        {
            push_back( ad, &d );
        }
        fclose( fp );
    }
}

int showMenuGetChoice( Cvec* ad )
{ 
    int num;
    char* tmp = NULL;

    fputs( MENU, stdout );
    while
    (
        printf( PROMPT ) &&
        (tmp = readLine(stdin)) != NULL &&
        ( (num = atoi(tmp)) < 1 || num > 10 )
    )
    {
        fputs( "Out of valid range 1..10  ", stdout );
        free(tmp);
    }
    free(tmp);

    if( num == 1 ) takeInDatAndFile( ad );
    else if( num == 2 )
    {
        int index;
        fputs( "Enter the name to find: ", stdout );
        tmp = readLine(stdin);
        if( (index = getNameIgnoreCase( ad, tmp )) == -1 )
            printf( "%s not found.n", tmp );
        else
            show( ad, index );
        free( tmp );
    }
    else if( num == 3 )
    {
        int index;
        fputs( "Enter the number to find: ", stdout );
        tmp = readLine(stdin);
        if( (index = getNumber( ad, tmp )) == -1 )
            printf( "%s not found.n", tmp );
        else
            show( ad, index );
        free( tmp );
    }
    else if( num == 4 ) showAll( ad );
    else if( num == 5 ) qsortNames( ad );
    else if( num == 6 ) qsortPhones( ad );
    else if( num == 7 ) chopDups( ad );
    else if( num == 8 ) editDel ( ad );
    else if( num == 9 ) writeSortedUnique( ad );
    /* else is 10 ... so will exit ... */
    
    return num;
}

/* validates that 10 char's are present and all are numeric ... */
int isValidPhoneNum( char ph[] )
{
    if( strlen(ph) != 10 ) return 0;
    for( ; *ph != 0; ++ph )
        if( *ph < '0' || *ph >'9' ) return 0;
    return 1;
}

/* ignore case compare function ... */
int compareIgnoreCase( char s1[], char s2[] )
{
    for( ; *s1 != 0 && *s2 != 0; ++s1, ++s2 )
    {
        if( tolower(*s1) != tolower(*s2) ) break;
    }
    /* ok ... check the end conditions ... */
    if( *s1 == 0  &&  *s2 == 0 ) return 0; /*  strings equal ...*/
    return tolower(*s1) - tolower(*s2);
}

/* returns index ... if found ... otherwise , returns -1 if NOT found */
int getNameIgnoreCase( Cvec* ad, char nameStr[] )
{
    int i;
    for( i = 0; i < ad->size; ++i)
        if( compareIgnoreCase(ad->ary[i].name, nameStr) == 0 )
            return i;
    return -1;
}

int getNumber( Cvec* ad, char numStr[] )
{
    int i;
    for( i = 0; i < ad->size; ++i)
        if( strcmp(ad->ary[i].phone, numStr) == 0 )
            return i;
    return -1;

}

void show( Cvec* ad, int i )
{
    printf
    (
        "<%03d> Name: %s,  Address: %s,  Phone: %sn",
        i+1, ad->ary[i].name, ad->ary[i].address, ad->ary[i].phone
    );
}
void showAll( Cvec* ad )
{
    int i;
    for( i = 0; i < ad->size; ++i ) show( ad, i );
    printf( "Employee records on file = %d :: Max capacity = %dnn",
            ad->size, ad->cap );
}

void takeInDatAndFile( Cvec* ad )
{
    int c, reply;
    Dat d;
    FILE* pFile = fopen( FILENAME, "a" );
    myAssert( (pFile != NULL), "Error: " FILENAME " not opened." );
    
    printf( "Enter name: " );
    d.name = readLine( stdin );
    printf("Enter address: ");
    d.address = readLine( stdin );
    for( ; ; )
    {
        printf( "Enter telephone: " );
        d.phone = readLine( stdin );
        if( isValidPhoneNum(d.phone) )
        {
            break;
        }
        /* else ... */
        fputs( "Only 10 digit number valid here ... ", stdout );
        free( d.phone );
    }

    /* Now we have good data ... so file and add to vector if ok ?  */
    fputs( "Ok ... (y/n) ? ", stdout );
    c = reply = tolower( getchar() );
    while( c != 'n' ) c = getchar(); /* flush stdin ... */
    if( reply == 'y' )
    {
        fprintf( pFile, "%sn",  d.name );
        fprintf( pFile, "%sn", d.address );
        fprintf( pFile, "%sn", d.phone );
        push_back( ad, &d );
        printf( "Information has been filed in file %s.n", FILENAME );
    }
    else
    {
        puts( "Aborted ..." );
        free( d.name );
        free( d.address );
        free( d.phone );
    }

    fclose( pFile );
}

void qsortNames( Cvec* ad )
{
    qsort( ad->ary, ad->size, sizeof(Dat), compareNameIgnoreCase );
    ad->isSorted = 1;
}
void qsortPhones( Cvec* ad )
{
    qsort( ad->ary, ad->size, sizeof(Dat), comparePhone );
    ad->isSorted = 0; /* i.e. NOT sorted by name ... */
}

int compareNameIgnoreCase( const void* x, const void* y )
{
    Dat* c1 = (Dat*) x;
    Dat* c2 = (Dat*) y;
    /* return strcmp(c1->name, c2->name); //or, this line only if need case// */
    int i = 0;
    while( c1->name[i] != 0 && c2->name[i] != 0 )
    {
        if( tolower(c1->name[i]) != tolower(c2->name[i]) )
            break;
        ++i;
    }
    /* now test end conditions .... */
    if( c1->name[i] == 0  &&   c2->name[i] == 0 ) /* i.e. if names the same */
    {
        return strcmp( c1->phone, c2->phone ); /* then use phone numbers ... */
    }
    /* else ... */
    return tolower(c1->name[i]) - tolower(c2->name[i]); /* else use next char */
}

int comparePhone( const void* x, const void* y )
{
    Dat* c1 = (Dat*) x;
    Dat* c2 = (Dat*) y;
    int compare = strcmp( c1->phone, c2->phone );
    if( compare == 0 ) /* if numbers the same ... */
        return strcmp( c1->name, c2->name ); /* then use names ... */
    /* else ... */
    return compare; /* use numbers ... since number's were NOT the same ...*/
}

void chopDups( Cvec* ad )
{
    int i, bot, top = ad->size-1;
    
    if(!ad->isSorted) qsortNames(ad); /* first make sure in name-sorted order */
    for( bot = 0; bot < top; ++bot )
    {
        i = bot;
        while( i < top && compareDat( &(ad->ary[bot]), &(ad->ary[i+1]) ) == 0 )
        {
            ++i; /* increment i while next rec. up stream is a duplicate ... */
        }
        /* if duplicate(s) ... copy last duplicate rec, upstream, down-over(bot)
           (and all following) ... and reset top, bot(at top of for loop) */
        if(i-bot)
        {
            int j = i-bot-1; /* but first free dynamic memory for duplicates */
            for( ; j >=0; --j )
            {
                free( ad->ary[bot+j].name );
                free( ad->ary[bot+j].address );
                free( ad->ary[bot+j].phone );
            }
            memcpy( &(ad->ary[bot]), &(ad->ary[i]), (top-i+1)*sizeof(Dat) );
            top -= (i-bot);
        }
    }
    /* finally ... re-set size to new (top+1) of unique number of rec's ... */
    /* and ... leave capacity unchanged ... (for now) ...  */
    ad->size = top+1;
}

int compareDat( Dat* x, Dat* y )
{
    int compare1 = strcmp( x->name, y->name ); /* first compare names ...*/
    if( compare1 == 0 ) /* if the same ... */
    {
        int compare2 = strcmp( x->address, y->address ); /* compare addresses */
        if( compare2 == 0 ) /* if the same ... */
            return strcmp( x->phone, y->phone ); /* use phone number then ... */
        /* else ...*/
        return compare2; /* use addresses after all  ... */
    }
    /* else ... */
    return compare1; /* use names ... since names were NOT ythe same ... */
}

void writeSortedUnique( Cvec* ad )
{
    int i;
    FILE* fp = fopen( "SU" FILENAME, "w" ); /* compiler concat's these TEXTs */
    myAssert( (fp != NULL), "Error: file SU" FILENAME " failed to open." );
    
    /* ok ... file is open ... so write all rec's to file ..*/
    
    chopDups( ad ); /* But first ... just to make sure sorted and unique ... */
    for( i = 0; i < ad->size; ++i )
    {
        fprintf( fp, "%sn", ad->ary[i].name );
        fprintf( fp, "%sn", ad->ary[i].address );
        fprintf( fp, "%sn", ad->ary[i].phone );
    }
    fclose( fp ); /* flushes buffer ... so all written to file now ... */
    printf( "n%d records filed in file SU%snn", i, FILENAME );
    
    /* and if have Windows OS ... can do this ... */
    system( "notepad SU" FILENAME ); /*compiler concat's text at compile time*/
}

void editDel( Cvec* ad )
{
    int c, reply, index;
    char* tmp;
    for( ; ; )
    {
        fputs( "Find by Name/Phone/Abort (n/p/a) ? ", stdout );
        c = reply = tolower(getchar());
        while( c != 'n' ) c = getchar(); /* flush stdin ... */

        if( reply == 'n' )
        {
            fputs( "Enter the name to find: ", stdout );
            tmp = readLine(stdin);
            if( (index = getNameIgnoreCase( ad, tmp )) == -1 )
                printf( "%s not found.n", tmp );
            else
            {
                show( ad, index );
                fputs( "Edit/Delete/Abort (e/d/a) ? ", stdout );
                c = reply = tolower(getchar());
                while( c != 'n' ) c = getchar(); /* flush stdin ... */

                if( reply == 'e' ) edit( ad, index );
                else if( reply == 'd' ) del( ad, index );
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'p' )
        {
            fputs( "Enter the number to find: ", stdout );
            tmp = readLine(stdin);
            if( (index = getNumber( ad, tmp )) == -1 )
                printf( "%s not found.n", tmp );
            else
            {
                show( ad, index );
                fputs( "Edit/Delete/Abort (e/d/a) ? ", stdout );
                c = reply = tolower(getchar());
                while( c != 'n' ) c = getchar(); /* flush stdin ... */

                if( reply == 'e' ) edit( ad, index );
                else if( reply == 'd' ) del( ad, index );
                else if( reply == 'a' )
                {
                    puts( "Ok, edit/delete aborted ..." );
                    free( tmp );
                    break;
                }
            }
            free( tmp );
        }
        else if( reply == 'a' )
        {
            puts( "Ok, edit/delete aborted ..." );
            break;
        }
        else printf( "Only n/p/a are valid ... " );
    }
}

void edit( Cvec* ad, int i )
{
    Dat d;
    int c, reply;
    for( ; ; )
    {
        printf( "Enter name: " );
        d.name = readLine( stdin );
        printf("Enter address: ");
        d.address = readLine( stdin );
        for( ; ; )
        {
            printf( "Enter telephone: " );
            d.phone = readLine( stdin );
            if( isValidPhoneNum(d.phone) ) break; /* out of INNER for loop ...*/
            /* else ... */
            fputs( "Only 10 digit number valid here ... ", stdout );
            free( d.phone );
        }
        /* ok ... we have some new data ... */
        fputs( "Ok ... (y/n) ? ", stdout );
        c = reply = tolower( getchar() );
        while( c != 'n' ) c = getchar(); /* flush stdin ... */
        if( reply == 'y' )
        {
            /* then edit ... but first free old ...  */
            free( ad->ary[i].name );
            free( ad->ary[i].address );
            free( ad->ary[i].phone );

            ad->ary[i].name =d.name;
            ad->ary[i].address =d.address;
            ad->ary[i].phone =d.phone;

            ad->isSorted = 0;
            break;
        }
        else
        {
            puts( "Aborted ..." );
            free( d.name );
            free( d.address );
            free( d.phone );
            break;
        }
    }
}

void del( Cvec* ad, int i )
{
    int c, reply;
    fputs( "Really delete/abort (d/a) ? ", stdout );
    for( ; ; )
    {
        c = reply = tolower(getchar());
        while( c != 'n' ) c = getchar(); /* flush stdin ... */
        if( reply == 'a' )
        {
            puts( "Delete aborted ..." );
            break;
        }
        else if( reply == 'd' )
        {
            /* first free dynamic memory */
            free( ad->ary[i].name );
            free( ad->ary[i].address );
            free( ad->ary[i].phone );

            memcpy( &(ad->ary[i]), &(ad->ary[i+1]), (ad->size-1-i)*sizeof(Dat) );
            -- ad->size;
            /* but leave cap unchamged  ... */
            break;
        }
        /* else ... */
        fputs( "Enter 'd' and WILL delete ... or 'a' to abort: ", stdout );
    }
    
    if( ad->size < 2 ) ad->isSorted = 1;
}




/* readLine.h */

/* http://developers-heaven.net/forum/index.php/topic,46.0.html */

/*
    Safe string data entry from file ... to replace gets and fgets
    BUT free new memory ... when done! Don't forget that C strings are
    pointers to a block of char, with a '�' char at the terminal end ...

    Call like this:

        char* line = readLine( stdin );

        or ...

        while( (line = readLine( FILEp )) )
        {
            // process line ...
        }
*/

#ifndef dwREADLINE
#define dwREADLINE

/* can re-set/adjust here to best match your line-length */
#define LINECHUNK 256

#ifndef dwSTDIO_H
#define dwSTDIO_H
#include <stdio.h>
#endif
#ifndef dwSTDLIB_H
#define dwSTDLIB_H
#include <stdlib.h>
#endif

#ifndef dwMYASSERT
#define dwMYASSERT
void myAssert( int condition, char text[] )
{
    if( !condition )
    {
        fprintf(stderr, "%sn", text );
        fputs( "Press 'Enter' to exit program ... ", stderr );
        getchar();
        exit(1);
    }
}
#endif

/* returns a string in new memory holding each word ... or NULL if EOF */
char* readLine( FILE* fp  )
{
    int c, i = 0; /* c to hold each char, i for index in string ... */
    int lineLen = LINECHUNK;
    void* tmp;
    char* strData = (char*) calloc( lineLen, 1 ); /* cast for C++ compilers */
    myAssert( (strData != NULL), "Error: calloc failed to allocate memory (1)" );
    while( (c = fgetc(fp)) != EOF  &&  c != 'n' )
    {
        if( i == lineLen-2 ) /* get more memory now (for word or line) */
        {
            lineLen += LINECHUNK; /* enlarge memory */
            if( (tmp = realloc(strData, lineLen)) == NULL )
            {
                free(strData);
                myAssert( 0, "Error: realloc failed to allocate memory (2)" );
            }
            strData  = (char*) tmp; /* good realloc above, so update pointer */
        }   
        strData[i] = c;
        ++i;
    }
    strData[i] = 0; /* confirm termination */

    if( c == EOF  && strData[0] == 0 ) { free( strData); return NULL; }
    /* else ... since didn't return NULL above, adjust string to right-size */
    if( (tmp = realloc(strData, i+1)) == NULL )
    {
        free(strData);
        myAssert( 0, "Error: realloc failed to allocate memory (3)" );
    }
    return (char*) tmp; /* return the right-sized string ... */
}

#endif


Was This Post Helpful? 0
  • +
  • -

#3 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

Re: C vector emulation

Posted 10 April 2010 - 10:14 PM

1. realloc used here for resize 2. some memory leaks were fixed 3. Cvec isSorted status added
Was This Post Helpful? 0
  • +
  • -

#4 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

Re: C vector emulation

Posted 11 April 2010 - 12:41 AM

The latest copy is maintained here: http://developers-he...index.php/topic,106.msg563.html#msg563
Was This Post Helpful? 0
  • +
  • -

#5 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

Re: C vector emulation

Posted 30 July 2011 - 02:32 PM

Update: NEW library link for Cvec.h, Cvec_func's.h (with function pointers), etc... http://developers-he...index.php/topic,2580.0.html
Was This Post Helpful? 0
  • +
  • -

#6 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 298
  • View blog
  • Posts: 1,839
  • Joined: 20-September 08

Re: C vector emulation

Posted 22 April 2012 - 04:54 PM

New links to latest Cvec (and example programs that use Cvec) ... http://developers-he...hp?topic=2022.0
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1