Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

home / study / engineering / computer science / questions and answers / lab 4: r

ID: 3824184 • Letter: H

Question

home / study / engineering / computer science / questions and answers / lab 4: requirements: compiles without warnings ... Your question has expired and been refunded. We were unable to find a Chegg Expert to answer your question. Question: Lab 4: Requirements: Compiles without warnings on ... Bookmark Lab 4: Requirements: Compiles without warnings on omega using gcc -std=c89 -g –pedantic filename.c Read in a class roster input file given as a command line argument to the program and save it into a queue data structure by using a linked listThe input file will be supplied and have a format as follows First_Name Last_Name ID Age Names will be no longer than 20 characters long The program must read from the file safely The program must gracefully exit if the input file command line parameter is not given The structs needed for the program are: struct student_record { int student_id_; int student_age_; char first_name_[21]; char last_name_[21]; }; struct student_record_node { struct student_record* record_; struct student_record_node* next_; }; Write a function to read in the class roster input file and create the queueIt will have the prototype: void parseFile(char* filename, struct student_record_node** head) Write a function that will print the contents of a struct student_record_nodeIt will have the prototype: void printNode(struct student_record_node* node) Write a function that will allocate and initialize a struct student_record_nodeIt will have the prototype: Struct student_record_node* student_record_allocate() Write a function that will free the memory occupied by a struct student_record_nodeIt will have the prototype: Void student_record_node_deallocate(struct student_record_node* node) Write a function that will sort the linked list by student ageIt will have the prototype: void sortByAge(struct student_record_node** recordsHead) Write a function that will sort the linked list by student idIt will have the prototype: void sortById(struct student_record_node** recordsHead) Write a function that the sorting functions will use for swapping the VALUES of nodes of a linked list(this is important. We are sorting by swapping values, not by changing position of a node. We will do actual position swapping in lab 5) void swap(struct student_record_node** node1, struct student_record_node** node2) Write a function that will free the memory occupied by the QueueIt will have the prototype: void freeNodeList(struct student_record_node* head) Write a function that will append a node to the QueueIt will have the prototype: void appendNode(struct student_record_node* head, struct student_record_node* newNode) Write a function that will print the QueueIt will have the prototype: void printNodeList(struct student_record_node* head) All source code will be in 1 file studentNumber_xyz_homework4.c where xyz are the student’s initials. Main must be the first function defined There must be NO memory leaks You MUST write safe code in regards to dynamic memory allocation AND pointers

Explanation / Answer

//main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "StudentRecordNode.h"
#include "StudentRecord.h"

typedef enum
{
   input_command_NEW_STUDENT,
   input_command_PRINT,
   input_command_MODIFY,
   input_command_REMOVE,
   input_command_SORTID,
   input_command_SORTAGE,
   input_command_SAVE,
   input_command_LOAD,  
   input_command_EXIT,
   input_command_ERROR
}input_command;


typedef enum
{  
   input_attribute_ID,
input_attribute_AGE,
input_attribute_FIRST,
input_attribute_LAST,
input_attribute_EXIT,
input_attribute_ERROR
}input_attribute;

int flag = 1;


void parseFile(char* filename, struct student_record_node** head);
int getLine(char * buffer, int length);
void displayMenu();
input_command acquireInputCmd();
void processCommand(input_command command, struct student_record_node** head);
int validateIntegerInput(int input, int lowBound, int highBound);
void getStudentInfo(struct student_record_node* node);
void requestStudentId(int* val);
void requestStudentAge(int* val);
void requestStudentFirstName(char * val, int length);
void requestStudentLastName(char * val, int length);
void loadRoaster(struct student_record_node** head);
void modifyNode(input_attribute trait, struct student_record_node* head);
void processAttribute(input_attribute trait, struct student_record_node* node);
void displayMenu2();
input_attribute acquireInputAtr();


int main(void)
{
   input_command command;
   struct student_record_node * head = NULL;
   while (1)
   {
       /* construct a main menu */
       displayMenu();

       /* capture user input */
       command = acquireInputCmd();
  
/* process the user input*/
       processCommand(command, &head);
   }
}


void parseFile(char* filename, struct student_record_node** head)
{//this creates the linked list in which we are storing the student record
   FILE* file;

  
   int nodeCount = 0;      
   struct student_record_node* first = NULL;  
   struct student_record_node* temp1 = NULL;
   struct student_record_node* temp2 = NULL;
   file = fopen(filename,"r");

   if(file == NULL)
   {
       printf("The file could not be opened. ");
       exit(1);  

   }

   while(fgets(filename, BUFSIZ, file) != NULL)
   {
      
if(nodeCount==0)//first node is different, it is not being connected to a node behind it
{
first = student_record_allocate();


sscanf(filename,"%s %s %d %d",first->record_->first_name_,first->record_->last_name_,&first->record_->student_id_,&first->record_->student_age_);
nodeCount ++;
continue;

}
      
       if(nodeCount == 1)
       {
           temp1 = student_record_allocate();
          
           sscanf(filename,"%s %s %d %d",temp1->record_->first_name_,temp1->record_->last_name_,&temp1->record_->student_id_,&temp1->record_->student_age_);
           appendNode(&first,temp1);
           nodeCount ++;
              
           continue;

       }
      
       if(nodeCount%2 == 0)
{
temp2 = student_record_allocate();

sscanf(filename,"%s %s %d %d",temp2->record_->first_name_,temp2->record_->last_name_,&temp2->record_->student_id_,&temp2->record_->student_age_);
           appendNode(&temp1,temp2);
nodeCount ++;
continue;

}

       if(nodeCount%2 != 0)
{
temp1 = student_record_allocate();

sscanf(filename,"%s %s %d %d",temp1->record_->first_name_,temp1->record_->last_name_,&temp1->record_->student_id_,&temp1->record_->student_age_);
appendNode(&temp2,temp1);
nodeCount ++;
        continue;   

}
      
      


      
   }

  
   *head = first;
   fclose(file);

     

}


/**
   This is where we capture user input and
   fetch the appropriate command based on
   that input
**/
input_command acquireInputCmd()
{
   char input[3];
   const int num_items = 3;
   int inputChoice;

   /**
       We check for certain conditions
           1. getline returning true
               This returns true when the user supplies input too large for our buffer. In our program,
               that is a security vulenerability, as our input must conform to size constraints.
           2. sscanf returning false
               This returns false when input doesn't conform to the format specifier string, meaning it is
               in the wrong format
   **/
   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%d", &inputChoice))
   {
       return input_command_ERROR;
   }

  

   if (validateIntegerInput(inputChoice, 1, 9)) //This checks the user input and if he is in the bounds of the options given
   {
       return (input_command)(inputChoice - 1);
   }
   else
   {
       /**
           If the user gave a number outside of the bounds of
           our expectations, we should return an error
       **/
       return input_command_ERROR;
   }
  
}

/**
   This is a bounded getline implementation. An unbounded implementation
   would keep track of the length of buffer and realloc when necessary. As such,
   it would need to pass a double pointer to buffer because realloc is not guranteed
   to return return an address to the existing buffer location. For our implementation,
   we only need the address of a buffer and the length of that buffer
**/

//buffer is temporary memory used to store data for this "job" or use

int getLine(char * buffer, int length)
{
   int charsRead = 0;
   int input = 0;
   int attemptedOverflow = 0;
   /**

           1. Input not being EOF because getc advances through the stream until it reaches the end
           2. Input not being a newline (that will move to what the user wants next)
           3. charsRead < length because we do not want to overflow our input buffer
   **/
   while ( (input = getc(stdin)) != EOF &&
       input != ' ' &&
       charsRead < length)
   {
       buffer[charsRead] = input;
       charsRead++;
   }

  
   //stdin needs to be flushed, fflush cannot be used because stdin is an input buffer

   if (input != ' ' && input != EOF)
   {
       attemptedOverflow = 1; //error!!
       while ((input = getc(stdin)) != EOF &&
           input != ' ') {
       }
   }

   return attemptedOverflow;
}

void addStudent(struct student_record_node** head)
{
   struct student_record_node* input = student_record_allocate();
   getStudentInfo(input);
   appendNode(head, input);
}


void getStudentInfo(struct student_record_node* node)
{
   requestStudentId(&node->record_->student_id_);
   requestStudentAge(&node->record_->student_age_);
   requestStudentFirstName(node->record_->first_name_, NAME_LENGTH);
   requestStudentLastName(node->record_->last_name_, NAME_LENGTH);
}

void requestStudentId(int* val)
{
   char input[ID_MAX_LENGTH];
   printf("Enter the Student ID ");

   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%d", val))
   {
       printf("Error. Please Try again .");
       requestStudentId(val);
   }
}

void requestStudentFirstName(char * val, int length)
{
   char input[NAME_LENGTH + 1] = { 0 };
   printf("Enter the Student First Name ");

   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%s", val))
   {
       printf("Error. Please Try again .");
       requestStudentFirstName(val, length);
   }
}

void requestStudentLastName(char * val, int length)
{
   char input[NAME_LENGTH + 1] = { 0 };
   printf("Enter the Student Last Name ");

   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%s", val))
   {
       printf("Error. Please Try again .");
       requestStudentLastName(val, length);
   }
}

void requestStudentAge(int * val)
{
   char input[AGE_MAX_LENGTH];
   printf("Enter the Student Age ");

   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%d", val))
   {
       printf("Error. Please Try again .");
       requestStudentAge(val);
   }
}


input_attribute acquireInputAtr()
{
   char input[3];
   const int num_items = 3;
   int inputChoice;

   if (getLine(input, sizeof(input)) ||
       !sscanf(input, "%d", &inputChoice))
   {
       return input_attribute_ERROR;
   }

  
   if (validateIntegerInput(inputChoice, 1, 5))
   {
       return (input_attribute)(inputChoice - 1);
   }
   else
   {
      
       return input_attribute_ERROR;
   }
  
}

void displayMenu2()
{
   printf("Please Enter Your Selection Below ");
   printf("_________________________________ ");
   printf("1. Change student ID. ");
   printf("2. Change student age. ");
   printf("3. Change student first name. ");
   printf("4. Change student last name. ");
   printf("5. Return to main menu. ");
  
}

void processAttribute(input_attribute trait, struct student_record_node* node)
{
      
  
   switch(trait)
   {
       case input_attribute_ID:
       {
           requestStudentId(&node->record_->student_id_);
           break;

       }

       case input_attribute_AGE:
       {
           requestStudentAge(&node->record_->student_age_);
           break;

       }
       case input_attribute_FIRST:
       {
           requestStudentFirstName(node->record_->first_name_,NAME_LENGTH);
           break;
       }
       case input_attribute_LAST:
       {
           requestStudentLastName(node->record_->last_name_,NAME_LENGTH);
           break;
       }
       case input_attribute_EXIT:
       {
           flag = 0;              

       }
       case input_attribute_ERROR:
       {
           printf("incorrect input. Try again ");
           break;
       }

       default:
       {
           printf("Theres a bug somewhere, we shouldn't be here ");
       }


   }


}


void modifyNode(input_attribute trait, struct student_record_node* head)
{
flag = 1; // using global variable flag, when true the modify node menu stays up

while (flag)
{

displayMenu2();
      
       trait = acquireInputAtr();
          
       processAttribute(trait,head);

      
      
}

  
}

void processCommand(input_command command, struct student_record_node** head)
{

   switch (command)
   {
       case input_command_NEW_STUDENT:
       {
           addStudent(head);
           break;
       }
       case input_command_PRINT:
       {
           printNodeList(*head);
           break;
       }
       case input_command_MODIFY:
       {  
          
           input_attribute trait;
           struct student_record_node* headptr = *head;
           headptr = findNode(headptr);
           if(headptr == NULL) /*checks if node exists*/
{
printf("node was not found ");
break;
}
  
          
           modifyNode(trait,headptr);
           break;  
       }
       case input_command_REMOVE:
       {
           struct student_record_node* headptr = *head;  
           headptr = findNode(headptr);
           if(headptr == NULL) /*checks if node exists*/
           {
               printf("node was not found ");
               break;  
           }  
           if(headptr->prev_ == NULL && headptr != NULL) /*checks if node is the first node in the linked link and if it exists*/
           {  
               *head = headptr->next_;
           }
           if(headptr != NULL) /*if node exists, remove it*/
           {
               removeNode(headptr);

           }
           student_record_node_deallocate(headptr);
           printf("node was removed ");  
           break;
       }
case input_command_SORTID: //using function pointer
       {
          
           int(*idPtr)(struct student_record_node*, struct student_record_node*)= &idComparator;
           sort(head,*idPtr);
      
           break;

       }
       case input_command_SORTAGE: //using function pointer
       {
           int(*agePtr)(struct student_record_node*, struct student_record_node*)= &ageComparator;
sort(head,*agePtr);
           break;


       }
       case input_command_SAVE:
       {
           FILE *fp;
char fnamer[100];
int len;


printf(" Please enter the file name, do not forget the .txt extension ");
fgets(fnamer,100,stdin); /*captures user input up to 100 characters*/
           len = strlen(fnamer);
if(len > 0 && fnamer[len-1] == ' ')
{
fnamer[len-1] = '';

}
           streamNodeList(fnamer,*head);
           break;
       }
       case input_command_LOAD:
{

FILE *fp;
char fnamer[100]; //name of file
int len;

freeNodeList(*head);
printf(" Please enter the file name or the full path to the file, do not forget the .txt extension ");
fgets(fnamer,100,stdin); /*captures user input up to 100 chars, the file name is stored in fnamer*/
len = strlen(fnamer);

if(len > 0 && fnamer[len-1] == ' ')
{
fnamer[len-1] = '';//set a terminating character so extra space is not counted

}
fp=fopen(fnamer,"r");//opening the file


if(fp==NULL)
{
printf(" File NOT FOUND! ",fnamer);

break;

}
parseFile(fnamer,head);
close(fp);
break;
}

      

       case input_command_EXIT:
       {
           printf("Exiting..Bye! ");
           freeNodeList(*head);
           exit(0);
           break;
       }
       case input_command_ERROR:
       {
           printf("incorrect input. Try again ");
           break;
       }

  
       default:
       {
           printf("Theres a bug somewhere, we shouldn't be here ");
       }
   };
}

/**
   True or false return
   This allows us to validate user input against a range of integers
*/
int validateIntegerInput(int input, int lowBound, int highBound)
{
   if (input >= lowBound && input <= highBound)
   {
       return 1;
   }
  
   else
   {
       return 0;
   }
}

/**
   The Main menu of the program
**/
void displayMenu()
{
   printf("Please Enter Your Selection Below ");
   printf("_________________________________ ");
   printf("1. Enter a new student. ");
   printf("2. Print Roster. ");
   printf("3. Modify an existing student's information. ");
   printf("4. Remove a student. ");
   printf("5. Sort roster by ID. ");
   printf("6. Sort roster by AGE. ");
   printf("7. Save current roster to a file. ");
   printf("8. Load a roster from a file. ");
   printf("9. Exit Program. ");
}

=======================================================================

//StudentRecord.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "StudentRecordNode.h"
#include "StudentRecord.h"


struct student_record_node* findNode(struct student_record_node* head)
{
int val;
int flag = 1;
char input[ID_MAX_LENGTH];
while(flag)
{
printf("Enter the Student ID ");

if (getLine(input, sizeof(input)) ||
!sscanf(input, "%d", &val))
{
printf("Error. Please Try again. ");
continue;
}
flag = 0;

}

while(head != NULL)
{
if(head->record_->student_id_ == val)
{
printf("found node ");
return head;
}
head = head->next_;

}

return NULL;

}

===========================================================================

//StudentRecord.h

#pragma once

struct student_record
{
   int student_id_;
   int student_age_;
   char* first_name_;
   char* last_name_;
};

enum student_record_constants
{
   NAME_LENGTH = 20,
   ID_MAX_LENGTH = 5,
   AGE_MAX_LENGTH =2
};

struct student_record_node* findNode(struct student_record_node* head);

===========================================================================

//StudentRecordNode.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "StudentRecordNode.h"
#include "StudentRecord.h"

void swap(struct student_record_node* node1,
struct student_record_node* node2)
{
   struct student_record_node* temp;

   if (node1->next_ == node2)
   {
       node1->next_ = node2->next_;
       node2->next_ = node1;
       node2->prev_ = node1->prev_;
       node1->prev_ = node2;
   }
   else if (node2->next_ == node1)
   {
       node2->next_ = node1->next_;
       node1->next_ = node2;
       node1->prev_ = node2->prev_;
       node2->prev_ = node1;
   }
   else
   {
       temp = node1->next_;
       node1->next_ = node2->next_;
       node2->next_ = temp;

       temp = node1->prev_;
       node1->prev_ = node2->prev_;
       node2->prev_ = temp;
   }

   if (node1->prev_)
   {
       node1->prev_->next_ = node1;
   }

   if (node1->next_)
   {
       node1->next_->prev_ = node1;
   }

   if (node2->prev_)
   {
       node2->prev_->next_ = node2;
   }

   if (node2->next_)
   {
       node2->next_->prev_ = node2;
   }
}


void student_record_node_deallocate(struct student_record_node* node)
{
   free(node->record_->first_name_);
   free(node->record_->last_name_);
   free(node->record_);
   free(node);
}


void appendNode(struct student_record_node** head,
struct student_record_node* newNode)
{
   struct student_record_node* iterator = *head;
   if (iterator == NULL)
   {
       *head = newNode;
       return;
   }
   while (iterator->next_ != NULL)
   {
       iterator = iterator->next_;
   }

   iterator->next_ = newNode;
   newNode->prev_ = iterator;
}

void printNode(struct student_record_node* node, char** string)
{
   if (!*string)
   {
       *string = calloc(200, sizeof(char));
   }
   sprintf(*string,
       "struct student_record_node:
       student first name: %s
       student second name: %s
       student id: %d
       student age: %d
       prevous: %p
       next: %p ",
       node->record_->first_name_,
       node->record_->last_name_,
       node->record_->student_id_,
       node->record_->student_age_,
       node->prev_,
       node->next_);
}

void freeNodeList(struct student_record_node* head)
{
   if (head == NULL)
   {
       return;
   }

   if (head->next_ != NULL)
   {
       freeNodeList(head->next_);
   }
   student_record_node_deallocate(head);
}

void streamNodeList(char * fileName, struct student_record_node* head)
{
   struct student_record_node* iterator = head;
   FILE* outputFile = fopen(fileName, "w");
   if (!outputFile)
   {
       printf("Cannot open output file. Exiting");
       exit(1);
   }

   if (head == NULL)
   {
       printf("Empty List ");
       return;
   }

   while (iterator != NULL)
   {
       char * output = calloc(1024, sizeof(char));
       printNode(iterator, &output);
       fwrite(output, sizeof(char), strlen(output), outputFile);
       iterator = iterator->next_;
       free(output);
   }

}

void printNodeList(struct student_record_node* head)
{
   struct student_record_node* iterator = head;
   char * output;
   if (head == NULL)
   {
       printf(" ");
       return;
   }

   output = calloc(1024, sizeof(char));
printNode(iterator, &output);
printf("%s", output);
printNodeList(iterator->next_);
free(output);


}

void sort(struct student_record_node** head, int(*compare_fcn)(struct student_record_node*, struct student_record_node*))
{
   struct student_record_node* iterator = NULL;
   int sorted = 0;
   do
   {
       sorted = 1;
       iterator = *head;
       while (iterator != NULL)
       {
           if (iterator->next_ != NULL)
           {
               if (compare_fcn(iterator, iterator->next_))
               {
                   if (iterator == *head)
                   {
                       *head = iterator->next_;
                   }
                   swap(iterator, iterator->next_);
                   sorted = 0;
               }
           }
           iterator = iterator->next_;
       }
   } while (!sorted);
}

int ageComparator(struct student_record_node* node1, struct student_record_node* node2)
{
   return (node1->record_->student_age_ > node2->record_->student_age_) ? 1 : 0;
}

int idComparator(struct student_record_node* node1, struct student_record_node* node2)
{
   return (node1->record_->student_id_ > node2->record_->student_id_) ? 1 : 0;
}

void removeNode(struct student_record_node* node)
{

      
   if(node->prev_)
   {
   node->prev_->next_ = node->next_;
   }
   if(node->next_)
   {
   node->next_->prev_ = node->prev_;
   }
}

struct student_record_node* student_record_allocate()
{
struct student_record_node* ret =
calloc(1, sizeof(struct student_record_node));

ret->record_ = calloc(1, sizeof(struct student_record));
ret->record_->first_name_ = calloc(1 + NAME_LENGTH, sizeof(char));
ret->record_->last_name_ = calloc(1 + NAME_LENGTH, sizeof(char));

return ret;
}

=======================================================================

//StudentRecordNode.h

#pragma once


struct student_record_node
{
struct student_record* record_;
struct student_record_node* next_;
struct student_record_node* prev_;
};


void swap(struct student_record_node* node1,struct student_record_node* node2);
void student_record_node_deallocate(struct student_record_node* node);
void appendNode(struct student_record_node** head,struct student_record_node* newNode);
void printNode(struct student_record_node* node, char** string);
void freeNodeList(struct student_record_node* head);
void streamNodeList(char * fileName, struct student_record_node* head);
void printNodeList(struct student_record_node* head);
void sort(struct student_record_node** head, int(*compare_fcn)(struct student_record_node*, struct student_record_node*));
int ageComparator(struct student_record_node* node1, struct student_record_node* node2);
int idComparator(struct student_record_node* node1, struct student_record_node* node2);
void removeNode(struct student_record_node* node);
struct student_record_node* student_record_allocate();

===============================================================