Q. Read in a class roster input file given as a command line argument to the pro
ID: 3823877 • Letter: Q
Question
Q. 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];
}; /** Should be defined in StudentRecord.h **/
struct student_record_node
{
struct student_record* record_;
struct student_record_node* next_;
struct student_record_node* prev_;
}; /** Should be defined in StudentRecordNode.h **/
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)
Should be defined in your .C file
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)
Should be defined in StudentRecordNode.h
Write a function that will write the contents of the queue linked list to a fileIt will have the prototype
void streamNodeList(char * fileName, struct student_record_node* head)
Should be defined in StudentRecordNode.h
Write a function that will allocate and initialize a struct student_record_nodeIt will have the prototype:
Struct student_record_node* student_record_allocate()
Should be defined in StudentRecordNode.h
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)
Should be defined in StudentRecordNode.h
Write a function that will sort the linked list by using a function pointer to a comparator functionIt will have the prototype:
void sort(struct student_record_node** recordsHead, int (*compare_fcn)(struct student_record_node*, struct student_record_node*))
Should be defined in StudentRecordNode.h
Write a function that will compare to nodes in the list and determine if the first node’s age is greater than the second node’s ageIt will have the prototype:
int ageComparator(struct student_record_node* node1, struct student_record_node* node2)
Should be defined in StudentRecordNode.h
Should return 1 or 0 accordingly
Write a function that will compare to nodes in the list and determine if the first node’s id is greater than the second node’s idIt will have the prototype:
int idComparator(struct student_record_node* node1, struct student_record_node* node2)
Should be defined in StudentRecordNode.h
Should return 1 or 0 accordingly
Write a function that the sorting functions will use for swapping the POSITIONS of nodes in a linked list(this is important. We are sorting by swapping changing position of nodes)
void swap(struct student_record_node** node1, struct student_record_node** node2)
Should be defined in StudentRecordNode.h
Write a function that will free the memory occupied by the QueueIt will have the prototype:
void freeNodeList(struct student_record_node* head)
Should be defined in StudentRecordNode.h
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)
Should be defined in StudentRecordNode.h
Write a function that will print the QueueIt will have the prototype:
void printNodeList(struct student_record_node* head)
Should be defined in StudentRecordNode.h
All source code will be in 3 files
studentNumber_xyz_homework5.c where xyz are the student’s initials.
StudentRecordNode.h
StudentRecord.h
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
Your program mustPerform all the same output as the previous lab BUT ADDITIONALLYWhenever you print the sorted linked lists to the terminal , you should ALSO WRITE THE LINKED LISTS TO A FILE
sort_age.txt
sort_id.txt
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();
=====================================================================
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.