Node:Example programs, Next:A note from the original author, Previous:Debugging, Up:Top
Example programs
The aim of this section is to provide a substantial example of C
programming, using input from and output to disk, GNU-style long
options, and the linked list data structure (including insertion,
deletion, and sorting of nodes).
#include <stdio.h>
#include <string.h>
#include <argp.h>
#define NAME_LEN 100
#define ADDR_LEN 500
const char *argp_program_version =
"bigex 1.0";
const char *argp_program_bug_address =
"<bug-gnu-utilsgnu.org>";
/* This structure is used by main to communicate with parse_opt. */
struct arguments
{
  char *args[1];            /* No arguments to this function */
  int verbose;              /* The -v flag */
  char *infile;             /* Argument for -i */
  char *outfile;            /* Argument for -o */
};
struct personal_data
{
  char name[NAME_LEN];
  char address[ADDR_LEN];
  struct personal_data *next;
};
/*
  OPTIONS.  Field 1 in ARGP.
  Order of fields: {NAME, KEY, ARG, FLAGS, DOC}.
*/
static struct argp_option options[] =
  {
    {"verbose", 'v', 0, 0, "Produce verbose output"},
    {"input",   'i', "INFILE",  0,
     "Read addresses from INFILE"},
    {"output",  'o', "OUTFILE", 0,
     "Output to OUTFILE instead of to standard output"},
    {0}
  };
/*
  PARSER. Field 2 in ARGP.
  Order of parameters: KEY, ARG, STATE.
*/
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
  struct arguments *arguments = state->input;
  switch (key)
    {
    case 'v':
      arguments->verbose = 1;
      break;
    case 'i':
      arguments->infile = arg;
      break;
    case 'o':
      arguments->outfile = arg;
      break;
    case ARGP_KEY_ARG:
      if (state->arg_num >= 1)
	{
	  argp_usage(state);
	}
      arguments->args[state->arg_num] = arg;
      break;
    case ARGP_KEY_END:
      if (state->arg_num < 1)
	{
	  argp_usage (state);
	}
      break;
    default:
      return ARGP_ERR_UNKNOWN;
    }
  return 0;
}
/*
  ARGS_DOC. Field 3 in ARGP.
  A description of the non-option command-line arguments
  that we accept.
*/
static char args_doc[] = "ARG";
/*
  DOC.  Field 4 in ARGP.
  Program documentation.
*/
static char doc[] =
"bigex -- Add ARG new names to an address book file.\vThe largest code example in the GNU C Tutorial.";
/*
  The ARGP structure itself.
*/
static struct argp argp = {options, parse_opt, args_doc, doc};
struct personal_data *
new_empty_node()
{
  struct personal_data *new_node;
  new_node =  (struct personal_data*)
    malloc (sizeof (struct personal_data));
  strcpy (new_node->name, "");
  strcpy (new_node->address, "");
  new_node->next = NULL;
  return new_node;
}
struct personal_data *
create_node()
{
  int bytes_read;
  int nbytes;
  struct personal_data *current_node;
  char *name;
  char *address;
  current_node = new_empty_node();
  puts ("Name?");
  nbytes = NAME_LEN;
  name = (char *) malloc (nbytes + 1);
  bytes_read = getline (&name, &nbytes, stdin);
  if (bytes_read == -1)
    {
      puts ("ERROR!");
    }
  else
    {
      strncpy (current_node->name, name, NAME_LEN);
      free (name);
    }
  puts ("Address?");
  nbytes = ADDR_LEN;
  address = (char *) malloc (nbytes + 1);
  bytes_read = getline (&address, &nbytes, stdin);
  if (bytes_read == -1)
    {
      puts ("ERROR!");
    }
  else
    {
      strncpy (current_node->address, address, ADDR_LEN);
      free (address);
    }
  printf("\n");
  return current_node;
}
struct personal_data *
find_end_node (struct personal_data *current_node)
{
  if (current_node->next == NULL)
    {
      return current_node;
    }
  else
    {
      return find_end_node (current_node->next);
    }
}
int
list_length (struct personal_data *root)
{
  struct personal_data *current_node;
  int count = 0;
  current_node = root;
  while (current_node->next != NULL)
    {
      current_node = current_node->next;
      count++;
    }
  return count;
}
struct personal_data *
find_node (struct personal_data *root,
	   int node_wanted)
{
  struct personal_data *current_node;
  int index = 0;
  current_node = root;
  while ((index < node_wanted) && (current_node->next != NULL))
    {
      current_node = current_node->next;
      index++;
    }
  return current_node;
}
delete_node (struct personal_data *root,
	     int location)
{
  struct personal_data *previous_node;
  struct personal_data *current_node;
  previous_node = find_node (root, location - 1);
  current_node = find_node (root, location);
  previous_node->next = current_node->next;
}
insert_node (struct personal_data *root,
	     struct personal_data *new_node,
	     int location)
{
  struct personal_data *temp_ptr;
  struct personal_data *previous_node;
  previous_node = find_node (root, location - 1);
  temp_ptr = previous_node->next;
  previous_node->next = new_node;
  new_node->next = temp_ptr;
}
swap_nodes (struct personal_data *root, int a, int b)
{
  int temp;
  struct personal_data *node_a;
  struct personal_data *node_b;
  struct personal_data *temp_node;
  if (a > b)
    {
      temp = a;
      a = b;
      b = temp;
    }
  node_b = find_node (root, b);
  delete_node (root, b);
  node_a = find_node (root, a);
  delete_node (root, a);
  insert_node (root, node_b, a);
  insert_node (root, node_a, b);
}
sort_list (struct personal_data *root)
{
  int i, j, list_len, diff;
  list_len = list_length (root);
  for (i=2; i<=list_len; i++)
    {
      j = i;
      while (strcmp ( (find_node(root, j))->name,
		      (find_node(root, j-1))->name) < 0)
	{
	  swap_nodes (root, j, j-1);
	  j--;
	}
    }
}
print_node (struct personal_data *current_node,
	    FILE *save_stream)
{
  fprintf (save_stream, "%s%s",
	   current_node->name,
	   current_node->address);
}
print_list (struct personal_data *current_node,
	    FILE *save_stream)
{
  print_node (current_node, save_stream);
  if (current_node->next != NULL)
    {
      print_list (current_node->next, save_stream);
    }
}
struct personal_data *
read_node (FILE *instream)
{
  int bytes_read;
  int nbytes;
  struct personal_data *current_node;
  char *name;
  char *address;
  char *blankline;
  int read_err = 0;
  current_node = new_empty_node();
  nbytes = NAME_LEN;
  name = (char *) malloc (nbytes + 1);
  bytes_read = getline (&name, &nbytes, instream);
  if (bytes_read == -1)
    {
      read_err = 1;
    }
  else
    {
      puts (name);
      strncpy (current_node->name, name, NAME_LEN);
      free (name);
    }
  nbytes = ADDR_LEN;
  address = (char *) malloc (nbytes + 1);
  bytes_read = getline (&address, &nbytes, instream);
  if (bytes_read == -1)
    {
      read_err = 1;
    }
  else
    {
      puts (address);
      strncpy (current_node->address, address, ADDR_LEN);
      free (address);
    }
  if (read_err)
    {
      return NULL;
    }
  else
    {
      return current_node;
    }
}
struct personal_data *
read_file (char *infile)
{
  FILE *input_stream = NULL;
  struct personal_data *root;
  struct personal_data *end_node;
  struct personal_data *current_node;
  root = new_empty_node();
  end_node = root;
  input_stream = fopen (infile, "r");
  if (input_stream)
    {
      while (current_node = read_node (input_stream))
	{
	  end_node->next = current_node;
	  end_node = current_node;
	  end_node->next = NULL;
	}
    }
  return root;
}
/*
  The main function.
  Notice how now the only function call needed to process
  all command-line options and arguments nicely
  is argp_parse.
*/
int
main (int argc, char **argv)
{
  struct arguments arguments;
  struct personal_data *root;
  struct personal_data *end_node;
  struct personal_data *current_node;
  int i, newnum;
  FILE *save_stream;
  /* Set argument defaults */
  arguments.infile = NULL;
  arguments.outfile = NULL;
  arguments.verbose = 0;
  /* Where the magic happens */
  argp_parse (&argp, argc, argv, 0, 0, &arguments);
  if (arguments.infile)
    {
      root = read_file (arguments.infile);
      end_node = find_end_node (root);
    }
  else
    {
      root = new_empty_node();
      end_node = root;
    }
  /* Where do we send output? */
  if (arguments.outfile)
    save_stream = fopen (arguments.outfile, "w");
  else
    save_stream = stdout;
  newnum = atoi (arguments.args[0]);
  for (i = 1; i <= newnum; i++)
    {
      current_node = create_node();
      end_node->next = current_node;
      end_node = current_node;
      end_node->next = NULL;
    }
  sort_list (root);
  print_list (root->next, save_stream);
  /* Close stream; skip error-checking for brevity of example */
  fclose (save_stream);
  /* If in verbose mode, print song stanza */
  if (arguments.verbose)
    {};
  return 0;
}