/******************************************************************************* 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 #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, 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 */