Node:Example programs, Next:, 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;
}