/****
 *
 * This program illustrates the dynamic allocation of string memory.  It uses
 * malloc to allocate strings of a proper size.  It uses calloc to allocate an
 * array big enough to hold the number of strings that the program reads in.
 *
 * The program takes two command-line arguments: the number of names to be read
 * and the file from which to read the names.  Each line of the file is
 * considered to be a separate name.  The length of a name can be up to 80
 * characters.  Any name longer than 80 characters is truncated to 80
 * characters.  Avoiding this truncation would require additional logic that
 * would overly complicate this example.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_NAME_LEN 81

int main(int argc, char** argv) {
    int num_names;              /* Number of names to read */
    char* filename;             /* Name of file to read from */
    FILE* file;                 /* File to read from */
    int i;                      /* Loop index variable */
    char name[MAX_NAME_LEN];    /* One name read from the file */
    char* name_sized;           /* Dynamically sized name string */
    char** names;               /* Array of all names read from the file */
    char* fstatus;              /* return value of fgets */

    /*
     * Check that there are two command-line args.
     */
    if (argc < 2) {
        printf("The program requires two arguments -- number and file.\n");
        return 1;
    }

    /*
     * Get the number of names to read and the name of the file to read from.
     * These are supplied by the program user in the first and second
     * command-line arguments.  Since argv is a string array, we need to call
     * atoi on argv[1], to convert it from a string to an int.  The filename in
     * argv[2] is good as a string.  This kind of command-line arg processing
     * was done in lab 7.
     */
    num_names = atoi(argv[1]);
    filename = argv[2];

    /*
     * Open the file, exiting of not openable.
     */
    if ((file = fopen(filename, "r")) == NULL) {
        printf("Cannot open file %s.\n", filename);
        return 1;
    }

    /*
     * Allocate a string array big enough to hold the number of names to be
     * read from the file.  This allocation is one of the main ideas of this
     * example.  It allows us to use an array of exactly the right size, rather
     * than declaring an array that tries to be big enough for any
     * eventuality.
     */
    names = (char**) calloc(num_names, sizeof(char*));

    /*
     * Read names from file, up to num_names or EOF, whichever occurs first.
     */
    for (i = 0, fstatus = fgets(name, MAX_NAME_LEN, file);
         i < num_names && fstatus != NULL;
         i++, fstatus = fgets(name, MAX_NAME_LEN, file)) {

        /*
         * Allocate a string just the right size for name.  This allocation is
         * the other main idea of this example.  It allows us to allocate
         * string storage that's exactly the size we need.  What we avoid is
         * having every name stored in a string of size MAX_NAME_LEN, when that
         * would be quite wasteful of space in most cases.
         */
        name_sized = (char*) malloc(strlen(name + 1));
        strcpy(name_sized, name);

        /*
         * Store the sized name in the names array.
         */
        names[i] = name_sized;
    }

    /*
     * Update the value of num_names to be the actual number of names read in,
     * in case it's different than the command-line arg value.
     */
    num_names = i;

    /*
     * Print out array to confirm that names were properly read.
     */
    for (i = 0; i < num_names; i++) {
        printf("%s", names[i]);
    }

    return 0;
}