/******************************************************************************* 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 expects the caller to provide space for the name, address, and phone strings instead of making the caller's pointers point to memory space that the function has provided as is done in the original version. *******************************************************************************/ #include #include #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, char *the_name, char *the_addr, char *the_phone ) INPUT: fp : file to read from OUTPUT: the_name : the NAME field of the entry the_addr : the ADDR field of the entry the_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 caller must ensure that the output parameters point to memory locations where there is sufficient space for the strings. The strings will never be longer than MAX_LEN characters. ---------------------------------------------------------------------------*/ int ReadRoloEntry( FILE *fp, char *the_name, char *the_addr, char *the_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; } strcpy( the_name, skip_white_space( name ) ); strcpy( the_addr, skip_white_space( addr ) ); strcpy( the_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; char name[MAX_LEN+1], addr[MAX_LEN+1], phone[MAX_LEN+1]; 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 */