/*

 * sort_city.c

 * This program reads city listings from a file and sorts them in several ways.

 */

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#define MAX_LINE 100

#define MAX_NAME 50

 

/* A city listing: */

struct _City

{

  char   *name;             /* The city name. */

  char   *country;          /* In which state it is located. */

  int    population;        /* Its population. */

};

 

typedef struct _City City;

 

/* A comparison function for two cities by their population: */

int comp_city_by_population (const void *p1, const void *p2)

{

  const City *p_city1 = *((const City **) p1);

  const City *p_city2 = *((const City **) p2);

 

  return (p_city2->population - p_city1->population);

}

 

/* A comparison function for two cities by their name: */

int comp_city_by_name (const void *p1, const void *p2)

{

  const City *p_city1 = *((const City **) p1);

  const City *p_city2 = *((const City **) p2);

 

  return (strcmp (p_city1->name, p_city2->name));

}

 

/* Prototypes: */

int read_cities (const char *filename, City ***p_p_cities, int *n_cities);

void free_cities (City **p_cities, int n_cities);

 

/* ------------------------------------------------------------------------

 * The main:

 */

int main ()

{

  City       **p_cities;       /* A vector of pointers to city objects. */

  int        n_cities;         /* Its size. */

  int        rc;

  int        i;

 

  /* Read the city listings from the input file "cities.txt". */

  rc = read_cities ("cities.txt", &p_cities, &n_cities);

 

  if (rc == -1)

  {

    /* File was not found. */

    printf ("Failed to open the input file 'cities.txt'.\n");

    return (1);

  }

  else if (rc == -2)

  {

    /* Illegal file format. */

    printf ("The input file 'cities.txt' has an illegal format.\n");

    return (1);

  }

 

  /* Sort the cities by their name. */

  qsort (p_cities, n_cities, sizeof (City *),

         comp_city_by_name);

 

  printf ("Cities sorted by name:\n");

  for (i = 0; i < n_cities; i++)

    printf ("    %s, %s, %d\n",

            p_cities[i]->name, p_cities[i]->country, p_cities[i]->population);

 

  getchar();

 

  /* Sort the cities by their population. */

  qsort (p_cities, n_cities, sizeof (City *),

         comp_city_by_population);

 

  printf ("Cities sorted by population:\n");

  for (i = 0; i < n_cities; i++)

    printf ("%3d) %s, %s, %d\n", i+1,

            p_cities[i]->name, p_cities[i]->country, p_cities[i]->population);

 

  /* Free memory. */

  free_cities (p_cities, n_cities);

 

  return (0);

}

 

/* ------------------------------------------------------------------------

 * Function: read_cities

 * Purpose:  Allocate and read a vector of pointers to city entries.

 * Input:    filename   - The input file name.

 * Output:   p_p_cities - The allocated vector of pointers.

 *           n_cities   - The number of cities read.

 * Returns:   1 - Success.

 *           -1 - File cannot be opened.

 *           -2 - Illegal file format.

 */

int read_cities (const char *filename,

             City ***p_p_cities, int *n_cities)

{

  FILE       *p_file;         /* The input file. */

  City       **p_cities;      /* The vector of city pointers. */

  char       line[MAX_LINE];  /* Auxiliary line buffer. */

  const char *p_comma1;       /* The location of the first ',' in the line. */

  const char *p_comma2;       /* The location of the second ',' in the line. */

  int        l_name;          /* Length of the current city name. */

  int        l_country;       /* Length of the current country name. */

  int        i;               /* Current city index. */

 

  *p_p_cities = NULL;

 

  /* Open the input file. */

  p_file = fopen (filename, "r");

 

  if (p_file == NULL)

    return (-1);

 

  /* The input file should the city listings, using the following format:

     | n

     | city-1,country-1,population-1

     | city-2,country-2,population-2

     |   :       :           :

     | city-n,country-n,population-n

 

     Read the number of cities. */

  if (fgets (line, MAX_LINE, p_file) == NULL)

  {

    fclose (p_file);

    return (-2);

  }

 

  *n_cities = atoi(line);

  if (*n_cities == 0)

  {

    fclose (p_file);

    return (-2);

  }

 

  /* Allocate memory for the city listings and read them. */

  p_cities = (City **) malloc (sizeof(City *) * (*n_cities));

 

  for (i = 0; i < *n_cities; i++)

  {

    /* Read the current line. */

    if (fgets (line, MAX_LINE, p_file) == NULL)

    {

      fclose (p_file);

      return (-2);

    }

 

    /* Analyze the listing - locate the delimiting comma characters. */

    p_comma1 = strchr (line, ',');

    p_comma2 = (p_comma1 != NULL) ? strchr (p_comma1 + 1, ',') : NULL;

 

    if (p_comma1 == NULL || p_comma2 == NULL)

    {

      fclose (p_file);

      return (-2);

    }

 

    /* Allocate the current city and set its information. */

    p_cities[i] = (City *) malloc (sizeof(City));

 

    l_name = p_comma1 - line;

    p_cities[i]->name = (char *) malloc (sizeof(char) * (l_name + 1));

    strncpy (p_cities[i]->name, line, l_name);

    p_cities[i]->name[l_name] = '\0';

 

    l_country = p_comma2 - p_comma1 - 1;

    p_cities[i]->country = (char *) malloc (sizeof(char) * (l_country + 1));

    strncpy (p_cities[i]->country, p_comma1 + 1, l_country);

    p_cities[i]->country[l_country] = '\0';

 

    p_cities[i]->population = atoi(p_comma2 + 1);

  }

 

  /* Successful termination. */

  fclose (p_file);

 

  *p_p_cities = p_cities;

  return (1);

}

 

/* ------------------------------------------------------------------------

 * Function: free_cities

 * Purpose:  Free a vector of pointers to city entries.

 * Input:    n_cities - The number of cities in the vector.

 * In/Out:   p_cities - The vector to be freed.

 * Returns:  Nothing.

 */

void free_cities (City **p_cities, int n_cities)

{

  int    i;

 

  /* Go over the vector of cities and free each one. */

  for (i = 0; i < n_cities; i++)

  {

    free (p_cities[i]->name);

    free (p_cities[i]->country);

    free (p_cities[i]);

  }

 

  /* Free the entire vector. */

  free (p_cities);

 

  return;

}