I have separated my solution into multiple files. You were not required to do this for your assignment, so I have also copied and pasted all of the source code into one big file and put it here. You are not required to ever split your C code across multiple files, but if in the future you read other people's code, you are very likely to see this happen.
I have also been careful to free up memory when done with it, which (if you were careful to only allocate what you needed) was also not required for the assignment.
Compiling Instructions
To compile the solution from multiple files, you should download all the files below as well as this makefile to the same directory and type 'make' on the command line.
Parliament.c
/*
* Parliament.c: this file contains the main program
*
* This program reads in a list of Members of Parliament
* and reports data about that list.
*
* Created on: 5-Feb-2009
* Author: Mark Hancock
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// These header files allow me to use the
// linked list code written elsewhere
#include "StringList.h"
#include "DataList.h"
// The head nodes for the linked lists containing the strings
struct StringListNode *occupation_head = NULL;
struct StringListNode *province_head = NULL;
struct StringListNode *caucus_head = NULL;
struct StringListNode *gender_head = NULL;
// The head node for the linked list of MPs
struct DataNode *data_head = NULL;
/*
Function: push
Purpose: Adds a new MP to the linked list of MPs.
Parameters:
occupation: the occupation of the MP
province: the province of the MP
caucus: the caucus of the MP
gender: the gender of the MP
Returns: None
*/
void push(const char *occupation,
const char *province,
const char *caucus,
const char *gender)
{
char *o = update_string_list(&occupation_head, occupation);
char *p = update_string_list(&province_head, province);
char *c = update_string_list(&caucus_head, caucus);
char *g = update_string_list(&gender_head, gender);
add_data(&data_head, o, p, c, g);
}
/*
Function: read_line
Purpose: Parses a line of text with the following format
<occupation><tab><province><tab><caucus><tab><gender>
Parameters:
buffer: a buffer containing a line of text in the format above
occupation: pointer to the array where the occupation is to be copied
province: pointer to the array where the province is to be copied
caucus: pointer to the array where the caucus is to be copied
gender: pointer to the array where the gender is to be copied
Returns: None
*/
void read_line(const char *buffer,
char *occupation,
char *province,
char *caucus,
char *gender)
{
sscanf(buffer, "%[^\t]\t%[^\t]\t%[^\t]\t%[^\t\r\n]",
occupation, province, caucus, gender);
}
/*
Function: read_data
Purpose: Reads all of the MP data from a file and stores it in a linked
list of MPs.
Parameters:
filename: the name of the file to read from
Returns: None
*/
void read_data(char *filename)
{
char occupation[50];
char province[50];
char caucus[50];
char gender[50];
char buffer[200];
FILE *file = fopen(filename, "r");
if (file == NULL)
{
fprintf(stderr, "Could not open file %s\n", filename);
exit(1);
}
while (fgets(buffer, 200, file) != NULL)
{
read_line(buffer, occupation, province, caucus, gender);
push(occupation, province, caucus, gender);
}
fclose(file);
}
/*
Function: print_usage
Purpose: Prints the usage of the program to stderr
Parameters:
progname: the name of the program
Returns: None
*/
void print_usage(char *progname)
{
fprintf(stderr, "Usage: %s [o|p|c|g|a]\n", progname);
exit(1);
}
/*
Function: main
Purpose: The main program.
Parameters:
argc: the number of arguments (including the program name)
argv: the array containing the arguments
Returns: 0 if successful, 1 otherwise
*/
int main(int argc, char **argv)
{
if (argc != 2 || strlen(argv[1]) != 1)
{
print_usage(argv[0]);
}
// read the data in from the file
read_data("mp-data.txt");
// check the first character of the first argument
switch (argv[1][0])
{
case 'a':
print_data_list(data_head);
break;
case 'o':
print_string_list(occupation_head);
break;
case 'p':
print_string_list(province_head);
break;
case 'c':
print_string_list(caucus_head);
break;
case 'g':
print_string_list(gender_head);
break;
default:
print_usage(argv[0]);
break;
}
clear_data_list(&data_head);
clear_string_list(&occupation_head);
clear_string_list(&province_head);
clear_string_list(&caucus_head);
clear_string_list(&gender_head);
return 0;
}
* Parliament.c: this file contains the main program
*
* This program reads in a list of Members of Parliament
* and reports data about that list.
*
* Created on: 5-Feb-2009
* Author: Mark Hancock
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
// These header files allow me to use the
// linked list code written elsewhere
#include "StringList.h"
#include "DataList.h"
// The head nodes for the linked lists containing the strings
struct StringListNode *occupation_head = NULL;
struct StringListNode *province_head = NULL;
struct StringListNode *caucus_head = NULL;
struct StringListNode *gender_head = NULL;
// The head node for the linked list of MPs
struct DataNode *data_head = NULL;
/*
Function: push
Purpose: Adds a new MP to the linked list of MPs.
Parameters:
occupation: the occupation of the MP
province: the province of the MP
caucus: the caucus of the MP
gender: the gender of the MP
Returns: None
*/
void push(const char *occupation,
const char *province,
const char *caucus,
const char *gender)
{
char *o = update_string_list(&occupation_head, occupation);
char *p = update_string_list(&province_head, province);
char *c = update_string_list(&caucus_head, caucus);
char *g = update_string_list(&gender_head, gender);
add_data(&data_head, o, p, c, g);
}
/*
Function: read_line
Purpose: Parses a line of text with the following format
<occupation><tab><province><tab><caucus><tab><gender>
Parameters:
buffer: a buffer containing a line of text in the format above
occupation: pointer to the array where the occupation is to be copied
province: pointer to the array where the province is to be copied
caucus: pointer to the array where the caucus is to be copied
gender: pointer to the array where the gender is to be copied
Returns: None
*/
void read_line(const char *buffer,
char *occupation,
char *province,
char *caucus,
char *gender)
{
sscanf(buffer, "%[^\t]\t%[^\t]\t%[^\t]\t%[^\t\r\n]",
occupation, province, caucus, gender);
}
/*
Function: read_data
Purpose: Reads all of the MP data from a file and stores it in a linked
list of MPs.
Parameters:
filename: the name of the file to read from
Returns: None
*/
void read_data(char *filename)
{
char occupation[50];
char province[50];
char caucus[50];
char gender[50];
char buffer[200];
FILE *file = fopen(filename, "r");
if (file == NULL)
{
fprintf(stderr, "Could not open file %s\n", filename);
exit(1);
}
while (fgets(buffer, 200, file) != NULL)
{
read_line(buffer, occupation, province, caucus, gender);
push(occupation, province, caucus, gender);
}
fclose(file);
}
/*
Function: print_usage
Purpose: Prints the usage of the program to stderr
Parameters:
progname: the name of the program
Returns: None
*/
void print_usage(char *progname)
{
fprintf(stderr, "Usage: %s [o|p|c|g|a]\n", progname);
exit(1);
}
/*
Function: main
Purpose: The main program.
Parameters:
argc: the number of arguments (including the program name)
argv: the array containing the arguments
Returns: 0 if successful, 1 otherwise
*/
int main(int argc, char **argv)
{
if (argc != 2 || strlen(argv[1]) != 1)
{
print_usage(argv[0]);
}
// read the data in from the file
read_data("mp-data.txt");
// check the first character of the first argument
switch (argv[1][0])
{
case 'a':
print_data_list(data_head);
break;
case 'o':
print_string_list(occupation_head);
break;
case 'p':
print_string_list(province_head);
break;
case 'c':
print_string_list(caucus_head);
break;
case 'g':
print_string_list(gender_head);
break;
default:
print_usage(argv[0]);
break;
}
clear_data_list(&data_head);
clear_string_list(&occupation_head);
clear_string_list(&province_head);
clear_string_list(&caucus_head);
clear_string_list(&gender_head);
return 0;
}
DataList.h
/*
* DataList.h
*
* This header file contains functions used to manipulate a linked
* list of members of parliament (MPs).
*
* Including this header file in another source file gives access
* to the functions defined in DataList.c
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#ifndef DATALIST_H_
#define DATALIST_H_
// The structure containing all of the MP information (defined in DataList.c)
struct DataNode;
/*
Function: add_data
Purpose: Adds an MP to the linked list of MPs. A new DataNode is allocated
on the heap, but no strings are allocated in memory. Thus, the occupation,
province, caucus, and gender should already point to valid memory space.
Parameters:
head: a pointer to the head pointer for this linked list
occupation: a pointer to a string containing the occupation
province: a pointer to a string containing the province
caucus: a pointer to a string containing the caucus
gender: a pointer to a string containing the gender
Returns: None
*/
void add_data(struct DataNode **head, char *occupation, char *province, char *caucus, char *gender);
/*
Function: print_data_list
Purpose: Prints the linked list of MPs.
Parameters:
head: the head pointer for this linked list
Returns: None
*/
void print_data_list(struct DataNode *head);
/*
Function: clear_data_list
Purpose: Frees up all of the memory used by this linked list structure.
Each node that has been allocated on the heap is removed (including
the head node) and the head node is made to point to NULLL. The strings
are left as-is.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None
*/
void clear_data_list(struct DataNode **head);
#endif /* DATALIST_H_ */
* DataList.h
*
* This header file contains functions used to manipulate a linked
* list of members of parliament (MPs).
*
* Including this header file in another source file gives access
* to the functions defined in DataList.c
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#ifndef DATALIST_H_
#define DATALIST_H_
// The structure containing all of the MP information (defined in DataList.c)
struct DataNode;
/*
Function: add_data
Purpose: Adds an MP to the linked list of MPs. A new DataNode is allocated
on the heap, but no strings are allocated in memory. Thus, the occupation,
province, caucus, and gender should already point to valid memory space.
Parameters:
head: a pointer to the head pointer for this linked list
occupation: a pointer to a string containing the occupation
province: a pointer to a string containing the province
caucus: a pointer to a string containing the caucus
gender: a pointer to a string containing the gender
Returns: None
*/
void add_data(struct DataNode **head, char *occupation, char *province, char *caucus, char *gender);
/*
Function: print_data_list
Purpose: Prints the linked list of MPs.
Parameters:
head: the head pointer for this linked list
Returns: None
*/
void print_data_list(struct DataNode *head);
/*
Function: clear_data_list
Purpose: Frees up all of the memory used by this linked list structure.
Each node that has been allocated on the heap is removed (including
the head node) and the head node is made to point to NULLL. The strings
are left as-is.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None
*/
void clear_data_list(struct DataNode **head);
#endif /* DATALIST_H_ */
DataList.c
/*
* DataList.c
*
* This source file contains functions used to manipulate a linked
* list of members of parliament (MPs) that were declared in DataList.h
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#include "DataList.h"
#include <stdlib.h>
#include <stdio.h>
/*
Each DataNode contains a pointer to four strings and a pointer to
the next DataNod in the linked list of MPs.
*/
struct DataNode
{
char *occupation;
char *province;
char *caucus;
char *gender;
struct DataNode *next;
};
/*
Function: add_data
Purpose: Adds an MP to the linked list of MPs. A new DataNode is allocated
on the heap, but no strings are allocated in memory. Thus, the occupation,
province, caucus, and gender should already point to valid memory space.
Parameters:
head: a pointer to the head pointer for this linked list
occupation: a pointer to a string containing the occupation
province: a pointer to a string containing the province
caucus: a pointer to a string containing the caucus
gender: a pointer to a string containing the gender
Returns: None
*/
void add_data(struct DataNode **head, char *occupation, char *province, char *caucus, char *gender)
{
struct DataNode *temp = malloc(sizeof(struct DataNode));
temp->next = *head;
temp->occupation = occupation;
temp->province = province;
temp->caucus = caucus;
temp->gender = gender;
*head = temp;
}
/*
Function: print_data_list
Purpose: Prints the linked list of MPs.
Parameters:
head: the head pointer for this linked list
Returns: None
*/
void print_data_list(struct DataNode *head)
{
struct DataNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
printf("%-30s%-30s%-30s%-30s\n", temp->occupation, temp->province, temp->caucus, temp->gender);
}
printf("\n");
}
/*
Function: clear_data_list
Purpose: Frees up all of the memory used by this linked list structure.
Each node that has been allocated on the heap is removed (including
the head node) and the head node is made to point to NULLL. The strings
are left as-is.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None
*/
void clear_data_list(struct DataNode **head)
{
struct DataNode *temp = *head;
struct DataNode *node_to_free = NULL;
while (temp != NULL)
{
node_to_free = temp;
temp = temp->next;
free(node_to_free);
}
*head = NULL;
}
* DataList.c
*
* This source file contains functions used to manipulate a linked
* list of members of parliament (MPs) that were declared in DataList.h
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#include "DataList.h"
#include <stdlib.h>
#include <stdio.h>
/*
Each DataNode contains a pointer to four strings and a pointer to
the next DataNod in the linked list of MPs.
*/
struct DataNode
{
char *occupation;
char *province;
char *caucus;
char *gender;
struct DataNode *next;
};
/*
Function: add_data
Purpose: Adds an MP to the linked list of MPs. A new DataNode is allocated
on the heap, but no strings are allocated in memory. Thus, the occupation,
province, caucus, and gender should already point to valid memory space.
Parameters:
head: a pointer to the head pointer for this linked list
occupation: a pointer to a string containing the occupation
province: a pointer to a string containing the province
caucus: a pointer to a string containing the caucus
gender: a pointer to a string containing the gender
Returns: None
*/
void add_data(struct DataNode **head, char *occupation, char *province, char *caucus, char *gender)
{
struct DataNode *temp = malloc(sizeof(struct DataNode));
temp->next = *head;
temp->occupation = occupation;
temp->province = province;
temp->caucus = caucus;
temp->gender = gender;
*head = temp;
}
/*
Function: print_data_list
Purpose: Prints the linked list of MPs.
Parameters:
head: the head pointer for this linked list
Returns: None
*/
void print_data_list(struct DataNode *head)
{
struct DataNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
printf("%-30s%-30s%-30s%-30s\n", temp->occupation, temp->province, temp->caucus, temp->gender);
}
printf("\n");
}
/*
Function: clear_data_list
Purpose: Frees up all of the memory used by this linked list structure.
Each node that has been allocated on the heap is removed (including
the head node) and the head node is made to point to NULLL. The strings
are left as-is.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None
*/
void clear_data_list(struct DataNode **head)
{
struct DataNode *temp = *head;
struct DataNode *node_to_free = NULL;
while (temp != NULL)
{
node_to_free = temp;
temp = temp->next;
free(node_to_free);
}
*head = NULL;
}
StringList.h
/*
* StringList.h
*
* This header file contains functions used to manipulate a linked
* list of strings.
*
* Including this header file in another source file gives access
* to the functions defined in StringList.c
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#ifndef STRINGLIST_H_
#define STRINGLIST_H_
// The structure for a node in a linked list of strings (defined in StringList.c)
struct StringListNode;
/*
Function: update_string_list
Purpose: Updates the linked list of strings to include this string. If the
linked list already contains a node for this string, it increments
the count by 1. If it does not, it adds a new node to this list with
for the given string, allocates a place on the heap to store this
string, and sets the count for this node to 1.
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to update
Returns: A pointer to a string allocated on the heap that is equal to 'str'.
*/
char *update_string_list(struct StringListNode **head, const char *str);
/*
Function: print_string_list
Purpose: Prints the linked list of strings and a total count for each one.
Parameters:
head: the head pointer for this linked list
Returns: None.
*/
void print_string_list(struct StringListNode *head);
/*
Function: clear_string_list
Purpose: Frees up the memory used by this linked list. Removes all of the nodes
used up by this linked list as well as the strings it allocates on the heap.
The head node is also freed up and then set to NULL.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None.
*/
void clear_string_list(struct StringListNode **head);
#endif /* STRINGLIST_H_ */
* StringList.h
*
* This header file contains functions used to manipulate a linked
* list of strings.
*
* Including this header file in another source file gives access
* to the functions defined in StringList.c
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#ifndef STRINGLIST_H_
#define STRINGLIST_H_
// The structure for a node in a linked list of strings (defined in StringList.c)
struct StringListNode;
/*
Function: update_string_list
Purpose: Updates the linked list of strings to include this string. If the
linked list already contains a node for this string, it increments
the count by 1. If it does not, it adds a new node to this list with
for the given string, allocates a place on the heap to store this
string, and sets the count for this node to 1.
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to update
Returns: A pointer to a string allocated on the heap that is equal to 'str'.
*/
char *update_string_list(struct StringListNode **head, const char *str);
/*
Function: print_string_list
Purpose: Prints the linked list of strings and a total count for each one.
Parameters:
head: the head pointer for this linked list
Returns: None.
*/
void print_string_list(struct StringListNode *head);
/*
Function: clear_string_list
Purpose: Frees up the memory used by this linked list. Removes all of the nodes
used up by this linked list as well as the strings it allocates on the heap.
The head node is also freed up and then set to NULL.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None.
*/
void clear_string_list(struct StringListNode **head);
#endif /* STRINGLIST_H_ */
StringList.c
/*
* StringList.c
*
* This source file contains functions used to manipulate a linked
* list of strings that were declared in StringList.c
*
* Two additional functions are defined to simplify the process of
* updating a node in the list: prepend_string & update
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#include "StringList.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/*
The structure for a node of the linked list of strings.
Contains a pointer to the string allocated on the heap, a count
of the total number of MPs using this string, and a pointer
to the next node in the list.
*/
struct StringListNode
{
char *str_data;
int count;
struct StringListNode *next;
};
/*
Function: prepend_string
Purpose: Adds a new node to the front of the linked list of strings.
A new node is allocated on the heap, as well as a new array
of characters (a.k.a. a string).
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to copy onto the heap
Returns: A pointer to the string on the heap.
*/
char *prepend_string(struct StringListNode **head, const char *str)
{
struct StringListNode *temp = malloc(sizeof(struct StringListNode));
temp->str_data = malloc((strlen(str) + 1) * sizeof(char));
strcpy(temp->str_data, str);
temp->count = 1;
temp->next = *head;
*head = temp;
return temp->str_data;
}
/*
Function: update
Purpose: Updates the count for the given string in the linked list if it is found.
Parameters:
head: the head pointer for this linked list
str: the string to update
Returns: A pointer to the corresponding string on the heap, if this node is found.
NULL, if the node is not found.
*/
char *update(struct StringListNode *head, const char *str)
{
struct StringListNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
if (strcmp(temp->str_data, str) == 0)
{
temp->count++;
return temp->str_data;
}
}
return NULL;
}
/*
Function: update_string_list
Purpose: Updates the linked list of strings to include this string. If the
linked list already contains a node for this string, it increments
the count by 1. If it does not, it adds a new node to this list with
for the given string, allocates a place on the heap to store this
string, and sets the count for this node to 1.
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to update
Returns: A pointer to a string allocated on the heap that is equal to 'str'.
*/
char *update_string_list(struct StringListNode **head, const char *str)
{
char *temp = update(*head, str);
if (temp == NULL)
{
// If node not found in this list, add it.
temp = prepend_string(head, str);
}
return temp;
}
/*
Function: print_string_list
Purpose: Prints the linked list of strings and a total count for each one.
Parameters:
head: the head pointer for this linked list
Returns: None.
*/
void print_string_list(struct StringListNode *head)
{
struct StringListNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
printf("%-30s%d\n", temp->str_data, temp->count);
}
printf("\n");
}
/*
Function: clear_string_list
Purpose: Frees up the memory used by this linked list. Removes all of the nodes
used up by this linked list as well as the strings it allocates on the heap.
The head node is also freed up and then set to NULL.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None.
*/
void clear_string_list(struct StringListNode **head)
{
struct StringListNode *temp = *head;
struct StringListNode *node_to_free = NULL;
while (temp != NULL)
{
node_to_free = temp;
temp = temp->next;
free(node_to_free->str_data);
free(node_to_free);
}
*head = NULL;
}
* StringList.c
*
* This source file contains functions used to manipulate a linked
* list of strings that were declared in StringList.c
*
* Two additional functions are defined to simplify the process of
* updating a node in the list: prepend_string & update
*
* Created on: 6-Feb-2009
* Author: Mark Hancock
*/
#include "StringList.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/*
The structure for a node of the linked list of strings.
Contains a pointer to the string allocated on the heap, a count
of the total number of MPs using this string, and a pointer
to the next node in the list.
*/
struct StringListNode
{
char *str_data;
int count;
struct StringListNode *next;
};
/*
Function: prepend_string
Purpose: Adds a new node to the front of the linked list of strings.
A new node is allocated on the heap, as well as a new array
of characters (a.k.a. a string).
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to copy onto the heap
Returns: A pointer to the string on the heap.
*/
char *prepend_string(struct StringListNode **head, const char *str)
{
struct StringListNode *temp = malloc(sizeof(struct StringListNode));
temp->str_data = malloc((strlen(str) + 1) * sizeof(char));
strcpy(temp->str_data, str);
temp->count = 1;
temp->next = *head;
*head = temp;
return temp->str_data;
}
/*
Function: update
Purpose: Updates the count for the given string in the linked list if it is found.
Parameters:
head: the head pointer for this linked list
str: the string to update
Returns: A pointer to the corresponding string on the heap, if this node is found.
NULL, if the node is not found.
*/
char *update(struct StringListNode *head, const char *str)
{
struct StringListNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
if (strcmp(temp->str_data, str) == 0)
{
temp->count++;
return temp->str_data;
}
}
return NULL;
}
/*
Function: update_string_list
Purpose: Updates the linked list of strings to include this string. If the
linked list already contains a node for this string, it increments
the count by 1. If it does not, it adds a new node to this list with
for the given string, allocates a place on the heap to store this
string, and sets the count for this node to 1.
Parameters:
head: a pointer to the head pointer for this linked list
str: the string to update
Returns: A pointer to a string allocated on the heap that is equal to 'str'.
*/
char *update_string_list(struct StringListNode **head, const char *str)
{
char *temp = update(*head, str);
if (temp == NULL)
{
// If node not found in this list, add it.
temp = prepend_string(head, str);
}
return temp;
}
/*
Function: print_string_list
Purpose: Prints the linked list of strings and a total count for each one.
Parameters:
head: the head pointer for this linked list
Returns: None.
*/
void print_string_list(struct StringListNode *head)
{
struct StringListNode *temp;
for (temp = head; temp != NULL; temp = temp->next)
{
printf("%-30s%d\n", temp->str_data, temp->count);
}
printf("\n");
}
/*
Function: clear_string_list
Purpose: Frees up the memory used by this linked list. Removes all of the nodes
used up by this linked list as well as the strings it allocates on the heap.
The head node is also freed up and then set to NULL.
Parameters:
head: a pointer to the head pointer for this linked list
Returns: None.
*/
void clear_string_list(struct StringListNode **head)
{
struct StringListNode *temp = *head;
struct StringListNode *node_to_free = NULL;
while (temp != NULL)
{
node_to_free = temp;
temp = temp->next;
free(node_to_free->str_data);
free(node_to_free);
}
*head = NULL;
}