/*******************************************************************************
   This is a modified version of the source code file rolofile.c which
   accompanies assignment # 3
   The difference is only in the function ReadRoloEntry() and in the code in
   the UNIT_TEST that tests this function. The version of ReadRoloEntry()
   in this file allocates space for the name, address, and phone strings
   instead of using the static internal buffers of the original version.
   This means that the caller of this function does not need to make a copy
   of the strings but instead the caller must free the string memory
   when the strings are no longer needed.
   Note: this implementation uses the function 'strdup()' which is not an
   ANSI-C function, however, it exists in most vendors' libraries.
   If it doesn't exist, you will have to write your own strdup() function to
   allocate space for the string and then copy it into that allocated space.
*******************************************************************************/

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

#define MAX_LEN	320	/* maximum # of characters in a field */

/***************************************************************************
NAME:		skip_white_space( const char *ptr )
INPUT:		ptr			: points to the current character
OUTPUT:		---
RETURN:		const char *		: points to the next
					  non-white-space character
DESCRIPTION:	Skips over white space in the string given as 'ptr'.

---------------------------------------------------------------------------*/
static	const char	*skip_white_space( const char *ptr )
{
	while ( isspace( *ptr ) )
		++ptr;

	return ( ptr );
}

/***************************************************************************
NAME:		read_field( FILE *fp, const char *field_name,
				char *field_value )
INPUT:		fp			: file to read from
		field_name		: name of the field to be read
OUTPUT:		field_value		: value of the field
RETURN:		int			: 1 if successfull
					  0 otherwise
DESCRIPTION:	Attempts to read the given field from the file.
		Note that the field value may have leading white-space.

---------------------------------------------------------------------------*/
static	int	read_field( FILE *fp, const char *field_name,
				char *field_value )
{
	int	num_matched;
	char	format[80];

	/* The following sprintf command creates a format string for the scanf.
	   Eg: If 'field_name' is "FOO", the format string would
	   be " FOO:%[^\n]". This would be used to check for a line
	   starting with "FOO:" and would read all the characters
	   following the ':' until the end of line.  */

	sprintf( format, " %s:%%[^\n]", field_name );
	/* The space before the %s is important ! */

	num_matched = fscanf( fp, format, field_value );

	return ( num_matched == 1 );
}


/***************************************************************************
NAME:		ReadRoloEntry( FILE *fp, const char **ptr_name,
				const char **ptr_addr, const char **ptr_phone )
INPUT:		fp			: file to read from
OUTPUT:		ptr_name		: the NAME field of the entry
		ptr_addr		: the ADDR field of the entry
		ptr_phone		: the PHONE field of the entry
RETURN:		int			: 0 if successfully read an entry
					 -1 on error or end of file
DESCRIPTION:	Reads one entry (consisting of a name, address, and phone)
		from the file.
		The NAME field must appear in the file and must be the first
		of the three lines defining the entry.
		The ADDR and PHONE fields are optional but the ADDR field
		must precede the PHONE field if both exist.

		Note that the output parameter values will point to memory that has
		been allocated by this function and that it is the caller's
		responsibility to free this memory when no longer needed.

---------------------------------------------------------------------------*/
int	ReadRoloEntry( FILE *fp, const char **ptr_name, const char **ptr_addr,
				const char **ptr_phone )
{
	int		status;
	char	name[MAX_LEN+1],
			addr[MAX_LEN+1],
			phone[MAX_LEN+1];

	/* Set all buffers to the empty string */

	name[0] = '\0';
	addr[0] = '\0';
	phone[0] = '\0';
	status = -1;

	if ( read_field( fp, "NAME", name ) )
	{
		read_field( fp, "ADDR", addr );
		read_field( fp, "PHONE", phone );
		status = 0;
	}

	*ptr_name = strdup( skip_white_space( name ) );
	*ptr_addr = strdup( skip_white_space( addr ) );
	*ptr_phone = strdup( skip_white_space( phone ) );

	return ( status );
}

/***************************************************************************
NAME:		WriteRoloEntry( FILE *fp, const char *name, const char *addr,
				const char *phone )
INPUT:		fp			: file to write to
		name			: the NAME field of the entry
		addr			: the ADDR field of the entry
		phone			: the PHONE field of the entry
OUTPUT:		---
RETURN:		int			: 0 if successfully wrote the entry
					 -1 on error
DESCRIPTION:	Writes one entry (consisting of name, address, and phone)
		to the file.
		Only non-empty fields are written to the file.

---------------------------------------------------------------------------*/
int	WriteRoloEntry( FILE *fp, const char *name, const char *addr,
				const char *phone )
{
	int	status = -1;

	if ( name != NULL && name[0] != '\0' )
	{
		fprintf( fp, "NAME: %.*s\n", MAX_LEN, name );

		if ( addr != NULL && addr[0] != '\0' )
			fprintf( fp, "ADDR: %.*s\n", MAX_LEN, addr );

		if ( phone != NULL && phone[0] != '\0' )
			fprintf( fp, "PHONE: %.*s\n", MAX_LEN, phone );

		fprintf( fp, "\n" );

		status = 0;
	}

	return ( status );
}

#ifdef UNIT_TEST
/***************************************************************************
This 'main' function is for use in doing a unit-test of the above functions.
The basic idea of a unit-test is that each module should be tested in isolation
from the others.  When this module is used as part of a larger program,
this main is not used - hence it is ifdef'd out.
---------------------------------------------------------------------------*/
int	main( int argc, char *argv[] )
{
	FILE		*fp;
	const char	*name, *addr, *phone;
	const char	*rolofile = "rolo.txt";

	if ( argc > 1 )
		rolofile = argv[1];

	fp = fopen( rolofile, "r" );
	if ( fp == NULL )
	{
		fprintf( stderr, "Couldn't open %s\n", rolofile );
		exit( 1 );
	}

	while ( ReadRoloEntry( fp, &name, &addr, &phone ) != -1 )
	{
		WriteRoloEntry( stdout, name, addr, phone );
		free( name );
		free( addr );
		free( phone );
	}

	if ( fclose( fp ) != 0 )
		fprintf( stderr, "Couldn't close %s\n", rolofile );

	return ( 0 );
}
#endif /* UNIT_TEST */

