/******************************************************************************* Source code accompanying Assignment # 3: This file contains functions for reading and writing rolodex file entries. You should not modify this file. The code that you write for the assignment should be put in another file. To create your program, you will then need to compile the two .c files (this file and the one you write) and link the resultant object files to form the executable. *******************************************************************************/ #include #include #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 get overwritten the next time this function is called so they should be copied by the caller if they are supposed to persist. ---------------------------------------------------------------------------*/ int ReadRoloEntry( FILE *fp, const char **ptr_name, const char **ptr_addr, const char **ptr_phone ) { int status; static 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 = skip_white_space( name ); *ptr_addr = skip_white_space( addr ); *ptr_phone = 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 ); } if ( fclose( fp ) != 0 ) fprintf( stderr, "Couldn't close %s\n", rolofile ); return ( 0 ); } #endif /* UNIT_TEST */