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;
}
 


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;
}
 


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;
}