2 Replies - 2340 Views - Last Post: 30 July 2011 - 04:32 PM Rate Topic: -----

#1 David W   User is offline

  • DIC supporter
  • member icon

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

C version, using a linked-list of struct Student

Posted 21 April 2009 - 04:22 AM

Description: C way to approximate class Student ... with an insert-sorted list and simple password protection.
Note: will only permit unique id's.
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* re. strlen, strcmp */

/*
    C/C++ students may like to see ...
    
    http://developers-heaven.net/forum/index.php/topic,127.0.html
    http://developers-heaven.net/forum/index.php/topic,134.0.html
    http://developers-heaven.net/forum/index.php/topic,106.0.html
    http://developers-heaven.net/forum/index.php/topic,46.0.html
*/

/* Globals ... */

#define header1 "Student GRADES MANAGEMENT SYSTEM"
#define header2 "1. (I)nitialize Student record list in RAMn" 
                "2. (A)dd i.e. insert in order, by name, a NEW Student recordn" 
                "3. view/(D)elete a Student recordn" 
                "4. view/(E)dit a Student recordn" 
                "5. (S)how all Student records (as inserted BY NAME above)n" 
                "6. (F)ile all Student records presently in RAMn" 
                "7. (R )ead all Student records on file into RAMn" 
                "8. set/change (P)asswordn" 
                "9. e(X)it"
                
#define fName "Studentinfo.txt"
#define fPW "StudentPW.txt"
#define minPWLen 8
#define maxStringLen 20

#define sortRead 1

typedef struct Student
{
    int id;
    char* last;
    char* first;
    float test1;
    float test2;
    float assignment;
    float labtest;
    float finalexam;
    struct Student* next;
}Student;

typedef Student* pStudent;

/* can use these global var's to keep function calls much simpler ... */
pStudent phead = NULL;
int numStudents = 0; /* to hold 'the number of Student records in memory' ...*/
int pwOK = 0;        /* to hold 'the password was OK' flag; 0=false & 1=true */
int fileFlag = 0;    /* fileFlag indicates 'the need to update the file' ... */

/* forward declarations */

pStudent pID(int n);
char* getLine( FILE* fp );
/* char* newCopy( char* str ); */
void add();
void insert( pStudent pS );
void flushStdin();
void showAll();
void viewEdit();
void viewDel();
void view(pStudent pS);
void edit(pStudent pS);
void del(pStudent pS);
void delAll();
void init();
void scramble( char s[] );
void unscramble( char s[] );
void writeAllToFile();
void exitNowYes();
int writeFile();
int readFile();
int passWord();
int newPW();
int StudentCmp( pStudent pS1, pStudent pS2 );

int menu()
{
    puts( header1 ); 
    if( numStudents == 1 ) puts( "Presently there is 1 record in RAM.n" );
    else printf( "Presently there are %d records.nn", numStudents );
    puts( header2 );
    printf( "nPlease enter your selection  : " );
    int selection = getchar();
    int c = selection;
    while( c!='n' ) c=getchar(); /* 'flush' stdin */
    return selection;
}


int main() /* main start * * * * * * * * * * * * * * * * * * * * * * * * * * */
{   
    int choice, readNum;
    pwOK = passWord();
    numStudents = readFile();
    
    for(;;) /* Loop forever ... until 'return' ... */
    {
        if( !pwOK ) 
            pwOK = passWord();
        
        choice = -1; /*  error flag */
        choice = menu();
        /* printf("You picked %d ... n", choice ); */
        switch( choice )
        {
            case '1': case 'i': case 'I': init(); break; /* out of 'switch' */
            case '2': case 'a': case 'A': add();  break;
            case '3': case 'd': case 'D': viewDel(); break;
            case '4': case 'e': case 'E': viewEdit(); break;
            case '5': case 's': case 'S': showAll(); break;
            case '6': case 'f': case 'F': writeAllToFile(); break;
            case '7': case 'r': case 'R':
            {
                /* If any were read ... will update Global variable phead */
                readNum = readFile(); 
                if( readNum > 0 )
                    numStudents = readNum; /* update Global numStudents */
            } 
            break;
            case '8': case 'p': case 'P': pwOK = newPW(); break; 
            case '9': case 'x': case 'X': exitNowYes(); break;
            default: puts("nNot implemented yet ...");
        } /* end switch */
    } /* end for ... */
    return 0;
} /* main end * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


/* Function definitions ... */

char* getLine( FILE* fp )
{
    char* buffer = (char*) calloc( maxStringLen+1, 1 );
    int i=0, c;
    buffer[0] = 0; /* NULL terminate 'as we go' ... */

    /* eats up WHOLE line ... including 'n' */
    while( (c=fgetc(fp)) != EOF && c != 'n' )
    {
        if( i<maxStringLen )
        {
            buffer[i] = c;
            buffer[i+1] = 0;
            ++i;
        }

    }

    return realloc( buffer, i+1  );
}

/*
char* newCopy( char* str )
{
    int i, len=strlen( str );
    //while( str[len]!=0 ) ++len;
    char * nCopy = (char*) malloc( len+1 );
    for( i=0; i<len; ++i )
        nCopy[i] = str[i];

    nCopy[len] = 0;
    return nCopy;
}
*/

/* Return a pointer to the Student if ID found in list; else return NULL */
pStudent pID( int n )
{
    pStudent p = phead;
    if( p==NULL )
        return NULL; /* List empty ... so all id numbers are available */
    
    while( p!=NULL )
    {
        if( p->id == n )
            return p; /* id number entered was already used */

        /* Else ... get next p to compare */
        p = p->next; 
    }  
        
    /* If reach here ... then id number not in list so ... */
    return NULL;
}


void flushStdin()
{
    while( getchar() != 'n' );
}

void exitNowYes()
{
    int c, quit;
    
    printf("nPress 'X' to eXit  ... ");
    c = quit = getchar();
    while(c != 'n') c=getchar(); /* flush stdin ... */
    if( quit=='x' || quit=='X' ) 
    {
        if( fileFlag )
            writeAllToFile();
            
        printf("nPress 'Enter' to exit right now ... ");
        if( (c=getchar()) == 'n' )
            exit(0);
        else
            while(c != 'n') c=getchar(); /* flush stdin ... */
    }
}

void add() /* ... and insert in the proper place in the list. */
{
    pStudent pS;
    int ID = 0;
    
    printf("Enter ID         : ");
    scanf("%d", &ID); flushStdin();

    if( pID(ID) || ID<=0  ) /* i.e. if pointer returned is NOT NULL, the id IS used */
    {
        printf("nid %d is NOT available.n", ID );
        return; /* Exit to menu right now ... */
    }
    /* If program reaches here, the ID just entered is available to use. */
    pS = (pStudent) malloc(sizeof(Student));
    pS->id = ID;
    
    printf("Enter Last Name  : ");
    pS->last = getLine(stdin);
    printf("Enter First Name : ");
    pS->first = getLine(stdin);
    
    printf("Enter Test1      : ");
    scanf( "%f", &pS->test1 );      flushStdin();       
    printf("Enter Test2      : ");
    scanf( "%f", &pS->test2 );      flushStdin();  
    printf("Enter Assignment : ");
    scanf( "%f", &pS->assignment ); flushStdin();       
    printf("Enter Lab Test   : ");
    scanf( "%f", &pS->labtest );    flushStdin();
    printf("Enter Final Exam : ");
    scanf( "%f", &pS->finalexam );  flushStdin();

    /* Set up link to this node */
    insert( pS );
}

/* insert in list with last & first names in proper order */
void insert( pStudent pS )
{
    pStudent q;
    
    /* Firstly, we handle the case where 'this' should be the first element. */
    if(phead == NULL || StudentCmp(pS, phead) < 0)
    {
        /* So ... it now becomes the first element ... */
        pS->next = phead; /* old phead becomes 2nd in list ... */
        phead = pS; /* and ... this pS ... becomes the head of the list */
    } 
    else /* If here ... search the linked list for the right location ... */
    { 
        q = phead; /* Get a working copy of phead in q */
        /* Start comparing with element AFTER 'q' ... i.e. the 'next in' ... */
        while(q->next != NULL && StudentCmp(q->next, pS) < 0)
        {
            q = q->next; /* Traverse the list ... */
        }
        /* 
            Ok, insert after 'q' by relinking the pointers (similar to above)
            ( Includes inserting at end position ... where q->next == NULL )
        */
        pS->next = q->next; /* Inserted 'pS' is linked to 'upstream element' */
        q->next = pS;  /* Now 'q' is updated to link to the new 'pS' element */
    } 
    
    /* Update global variables. */
    ++numStudents;
    fileFlag = 1;
}

void showAll()
{
    pStudent p = phead;
    int c;

    if (phead==NULL) 
    { 
        puts("nNo records in memory at present.") ; 
        return; 
    }
    
    /* If reach here ... */
    
    while( p!=NULL )
    {
        view( p );
        printf("nPress 'Enter' to continue ... or enter 'A' to abort ... ");
        if( (c=getchar()) == 'a' || c == 'A' ) 
        {
            flushStdin();
            return;
        }
        while(c != 'n') c=getchar(); /* flush stdin ... */
        p = p->next;
    }
}

void viewEdit()
{
    int yes = 0, ID = 0;
    pStudent p;
    
    printf("Enter the id number to View/Edit : ");
    scanf("%d",&ID); flushStdin();
    
    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p )     /* i.e. if pointer returned above was not NULL ... */
    { 
        view(p);
        printf("nEdit (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' ) 
        {
            while( yes!='n' ) yes=getchar(); /* flush stdin */
            edit(p);
        }
        else while( yes!='n' ) yes=getchar(); /* flush stdin */
    }
    else printf("nStudent with ID number %d not found.n", ID);
}

void view(pStudent pS)
{
    printf
    (
        "ID number  : %dn"
        "Last Name  : %sn"
        "First Name : %sn"
        "Test 1     : %.2fn"
        "Test 2     : %.2fn"       
        "Assignment : %.2fn"
        "Lab Test   : %.2fn"
        "Final Exam : %.2fn",
        pS->id, pS->last, pS->first,
        pS->test1, pS->test2, pS->assignment,
        pS->labtest, pS->finalexam
    );   
}

void edit(pStudent pS)
{
    int ID;
    int idTmp = pS->id; /* Firstly get a backup copy of this id ... */
    
    pS->id = -1; /* Now ... reset old id number to a dummy value ... */

    printf("Edit ID          : ");
    ID = 0;
    scanf("%d", &ID); flushStdin();
    
    if( pID(ID) ) /* i.e. if pointer returned is not NULL, this 'ID' IS USED */
    {
        printf("nid %d is NOT available.n", ID );
        pS->id = idTmp; /* Restore the id since this pS was NOT  edited */
        return; /* Exit to menu right now ... */
    }
    
    /* If reach hear ... */
    
    del(pS);/* Delete old record ... etc */
    
    pS = (pStudent) malloc(sizeof(Student)); /* get new memory for new record */
    pS->id = ID;
    
    printf("Edit Last Name   : ");
    pS->last = getLine(stdin);
    printf("Edit First Name  : ");
    pS->first = getLine(stdin);      

    printf("Edit Test1       : ");
    scanf( "%f", &pS->test1 );      flushStdin();       
    printf("Edit Test2       : ");
    scanf( "%f", &pS->test2 );      flushStdin();  
    printf("Edit Assignment  : ");
    scanf( "%f", &pS->assignment ); flushStdin();       
    printf("Edit Lab Test    : ");
    scanf( "%f", &pS->labtest );    flushStdin();
    printf("Edit Final Exam  : ");
    scanf( "%f", &pS->finalexam );  flushStdin();
    
    insert( pS );
}

void viewDel()
{
    pStudent p;
    int yes = 0, ID = 0;

    printf("Enter the id number to View/Delete : ");
    scanf("%d",&ID); flushStdin();
    
    p = pID(ID); /* See if pointer exists; i.e. get value or NULL */
    if( p ) /* i.e. if pointer returned above was not NULL ... */
    { 
        view(p);
        printf("nDelete (y/n) ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' ) { del(p); fileFlag = 1; }
        while( yes!='n' ) yes=getchar(); /* flush stdin */
    }
    else printf("nStudent with ID number %d not found.n", ID);
}

void del(pStudent pS)
{
    pStudent p,
             pp; /* to hold the pointer to the previous record */

    /* Handle special case of 'first in list' */
    if( pS==phead )
    {
        phead = pS->next;
        free( pS->first );
        free( pS->last );
        free( pS );
        --numStudents; /* update globals ... */
        fileFlag = 1;
        return;   
    }
    
    /* Else not first in list, so ... */
    
    p = phead; /* set p to this initial value to start loop */
    
    /* Now find the pointer to the previous record. */
    while( p != pS )
    {
        pp = p; /* pp holds previous pointer value ... */
        p = p->next; /* set to next pointer in link list chain */
    }
    
    /* 
        Now we have a pointer to the previous Student record, so ...
        we can now point that previous record to one past this pS record            
    */
    pp->next = pS->next;
    
    /* Now free the memory for this record and update the globals ... */
    free( pS->first );
    free( pS->last );
    free( pS);
    --numStudents;
    fileFlag = 1;
}

void delAll()
{
    pStudent p = phead; /* Set p to this initial value to start loop */
    pStudent pNext;     /* To hold the pointer to the next record */
    
    while( p != NULL )
    {
        pNext = p->next; /* Get a pointer to the next record. */
        free( p->first );
        free( p->last );
        free( p );
        p = pNext; /* Update p ... */
    }
    
    /* Update globals ...  */
    phead = NULL;
    numStudents = 0;
    fileFlag = 0; 
}

/* Note: calling delAll() here ... will re-set globals as above ... */
void init() 
{
    int yes;
    if( phead != NULL )
    {
        printf("nDo you wish to overwrite the records in memory y/n ? ");
        yes = getchar();
        if( yes=='y' || yes=='Y' )
            delAll(); /* re-sets globals ... */
        else
        {
            if(numStudents==1)
                puts("1 Student record was left intact in memory.");
            else
                printf("%d Student records were left intact in memory.n",
                       numStudents);
        }   
        while( yes!='n' ) yes=getchar(); /* flush stdin */ 
    }
    else puts("nThere were no records in memory to clear.");
}

void scramble( char s[] )
{
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] - code[i]); ++i; }
}

void unscramble( char s[] )
{    
    int i = 0;
    int code[] = {3,1,4,1,5,9,8,6,7,0,7,0,2,8,6,9,5,3,4,2};
    while( s[i]!=0 ) { s[i] = (char) ((int)s[i] + code[i]); ++i; }
}

void writeAllToFile()
{
    if( !fileFlag ) return;
    
    if( numStudents == writeFile() )
    {
        if( numStudents > 0 )
        {
            if( numStudents == 1 )
                puts("nThe 1 Student record was safely filed.");
            else
                printf
                (
                    "nAll %d Student records safely filed.n",
                    numStudents
                );
        }    
    }
    else
    {
        printf
        (
            "nAn error occured while filing the Student records."
            "nPlease see the programmer for help with the problem.n"
            "nExiting the program now.  Press 'Enter' to continue ... "
        );
        flushStdin(); /* Holds window open to show above message. */
        exit(0); /* Return error number to system. */
    }    
}

int writeFile()
{    
    int count; /* to track the records actually written to the file */
    FILE* fp;
    pStudent p = phead;

    if( phead==NULL )
    { 
        puts("nNo records available ... so NO records written to file.") ; 
        return 0; 
    }
    
    fp = fopen( fName, "w" );
    if( fp==NULL )
    {
        printf("nError opening file %s.  Press 'Enter' to continue ... ", fName);
        flushStdin();
        return 0;
    }
    
    count = 0; /* to track the records actually written to the file */
    while( p!=NULL )
    {
        fprintf
        (
            fp,
            "%dn"
            "%sn"
            "%sn"
            "%.2fn"
            "%.2fn"       
            "%.2fn"
            "%.2fn"
            "%.2fn",
            p->id, p->last, p->first,
            p->test1, p->test2, p->assignment,
            p->labtest, p->finalexam
        );
        
        ++count;
        p = p->next;
    }
    
    fclose( fp );
    fileFlag = 0;
    return count; /* Number of records written. */
}

int readFile()
{
    int count, ID;
    //char buffer[maxStringLen+1];
    FILE *fp;

    pStudent pS;
#if !sortRead
    pStudent pp=NULL;
#endif

    fp = fopen( fName, "r" );
    if( fp==NULL )
    {
        printf
        (
            "nError opening file %s.n"
            "Perhaps it hasn't been created yet?n"
            "Press 'Enter' to continue ... ", 
            fName
        );
        flushStdin();
        return 0; 
    }
    
    /* BUT!!! may need to delete any records in memory first. */
    init();
    
    /* 
        NOTE: we need phead to be equal to NULL in the following ...
        to be permitted to set phead ...  
        to point to the first Student record in memory (read in from the file).
    */
    if( phead != NULL ) /* then exit with message ... */
    {
        if(numStudents==1)
            puts("nThe 1 former Student record was left in memory.");
        else
            printf("nThe %d former Student records were left in memory.n", numStudents);
        return 0; /* So ...old count of Student records will NOT be updated. */
    }
    
    /* If the program reaches here ... then ... */
    
    count = 0;

    while(  fscanf( fp, "%d", &ID) != EOF  )
    {
        pS = (pStudent) malloc(sizeof(Student));
        pS->id = ID;
        
        //fscanf( fp, "%s", buffer );
        //pS->last = newCopy( buffer );
        fgetc(fp); /* eats up the 'n' char left over from above 'ID read' */
        pS->last = getLine(fp);

        //fscanf( fp, "%s", buffer );
        //pS->first = newCopy( buffer );
        pS->first = getLine(fp);

        fscanf( fp, "%f", &pS->test1 );      
        fscanf( fp, "%f", &pS->test2 ); 
        fscanf( fp, "%f", &pS->assignment );        
        fscanf( fp, "%f", &pS->labtest );
        fscanf( fp, "%f", &pS->finalexam );
#if !sortRead            
        if ( pp != NULL )   /* for 2nd and up records ...*/
            pp->next = pS;  /* now previous record points to this new one */ 
        else phead =pS;     /* first record only has base pointer */
            
        pS->next = 0; /* NULL terminate list ... */
        pp = pS; /* Keep a copy of address of this pS in pp for next while loop. */
#else
        insert( pS ); 
#endif      
        ++count;
        /* printf( "nRecord number is %dn", count ); */
    }
    fclose( fp );
    if(count==1)
        puts("n1 record was read into memory from the disk file.");
    else
        printf("n%d records were read into memory from the disk file.n", count);
        
    fileFlag = 0;
    return count; /* Total number of Student records found in the file. */
}

int passWord()
{
    /* 
        Get the password in the file, if it exists, into buffer2 
        and compare it with the user entry in buffer1. 
    */
    char *buffer1;
    char buffer2[maxStringLen+1];
    int attempts;

    int pwOK = 0;
    FILE *fp;
    fp = fopen(fPW, "r"); 

    if(fp==NULL) /* i.e. if file fPW doesn't exit ... */
    {
        pwOK = newPW(); /* get new password ...*/
        if( pwOK == 1 ) return 1; /* report this match of passwords back ...*/
    }
    else /* File fPW does exist ... so ... */
    {
        /* Get file password into a string in buffer2. */
        fscanf( fp, "%s", buffer2 );
        fclose( fp );
        unscramble( buffer2 );
        
        /* Now ... get password entered by user into a string in buffer1. */
        for( attempts=0; attempts<3; ++attempts )
        {
            printf("Enter password: ");
            //scanf( "%s", buffer1 ); flushStdin();
            buffer1 = getLine(stdin);
            if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched. */
            {
                puts( "Match!n" );
                free( buffer1 );
                return 1; /* Report this match of passwords back ...*/
            }
            free( buffer1 );
        }
    }
    /* if reach here ...*/
    
    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    exit(1); /* Quit the program right now ... */
}

int newPW()
{
    FILE* fp;
    char* buffer1;
    char* buffer2;
    
    for(;;)
    {
         /* Get the new password into a string in buffer1. */
        printf("Enter the new password (8 to 20 characters): ");
        buffer1 = getLine(stdin); /* a max of maxStringLen char's */
        if( strlen(buffer1) >= minPWLen )
            break;
        
        printf("Your password must be at least %d characters ...n", minPWLen );
        free( buffer1 ); /* ... and try again ...*/
    }
    
    /* 
        Get a 'verify copy' of the new password into buffer2 
        and compare it with the password in buffer1. 
    */
    printf("Enter the new password again: ");
    buffer2 = getLine(stdin);
    if(strcmp(buffer1, buffer2)==0) /* If true ... passwords matched. */
    {
        fp = fopen(fPW, "w"); 
        if(fp==NULL)
        {
            printf("Error opening file %s ... Press 'Enter' to exit ... ", fPW);
            flushStdin();
            free(buffer2);
            free(buffer1);
            exit(2);
        }
        else
        {
            puts( "Match!n" ); 
            scramble(buffer1);
            fprintf( fp, "%s", buffer1 );       
            fclose( fp );
            free(buffer2);
            free(buffer1);
            return 1; /* Report this match of passwords back ...*/
        }
    }
    
    /* If reach here ...*/
        
    printf( "NO MATCH! ... Press 'Enter' to exit ... " );
    flushStdin();
    free(buffer2);
    free(buffer1);
    exit(3); /* Quit the program right now ... */
}

/* A function to compare two Student records to permit sorting ... */
int StudentCmp( pStudent pS1, pStudent pS2 )
{
    int compare = strcmp(pS1->last, pS2->last);
    if ( compare == 0 )
        return strcmp(pS1->first, pS2->first);
    
    return compare;
}



Is This A Good Question/Topic? 0
  • +

Replies To: C version, using a linked-list of struct Student

#2 David W   User is offline

  • DIC supporter
  • member icon

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

Re: C version, using a linked-list of struct Student

Posted 06 March 2010 - 08:45 PM

I need to fix/update this so that it will handle the case of a failure to allocate memory ... For now, see readWord at: http://www.dreaminco...snippet5069.htm and readLine at the bottom of: http://www.dreaminco...snippet5142.htm
Was This Post Helpful? 0
  • +
  • -

#3 David W   User is offline

  • DIC supporter
  • member icon

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

Re: C version, using a linked-list of struct Student

Posted 30 July 2011 - 04:32 PM

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

Page 1 of 1