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