P
Paul D. Boyle
Hi all,
There was a recent thread in this group which talked about the
shortcomings of fgets(). I decided to try my hand at writing a
replacement for fgets() using fgetc() and realloc() to read a line of
arbitrary length. I know that the better programmers in this group could
write a more robust function, but here is my shot at it anyway.
I would appreciate people's comments on my fget_line() code below
(usage example included). Any constructive criticism welcome regarding
logic, design, style, etc. Thanks.
Paul
/* fget_line(): a function to read a line of input of arbitrary length.
*
* Arguments:
* 'in' -- the input stream from which data is wanted.
* 'buf' -- the address of a pointer to char. The read in results
* will be contained in this buffer after the fget_line returns.
* *** THE CALLER MUST FREE THIS POINTER ***
* 'sz' -- the caller can supply an estimate of the length of line to be
* read in. If this argument is 0, then fget_line() uses a
* default.
* 'validate' -- a user supplied callback function which is used to validate
* each input character. This argument may be NULL in which
* case no input validation is done.
*
* RETURN values:
* fget_line() on success: returns the number of bytes read
* realloc() related failure: returns -1 (#define'd below as ERROR_MEMORY)
* illegal input: returns -2 (#define'd below as ERROR_ILLEGAL_CHAR)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifndef USER_DIALOG_H /* if I am not using this as part of my
* 'user_dialog' utilities.
*/
#define LINE_LEN 80
#define DELIMITER '\n'
#define ERROR_MEMORY (-1)
#define ERROR_ILLEGAL_CHAR (-2)
#else
#include "user_dialog.h"
#endif
int fget_line( FILE *in, char **buf, size_t sz, int (*validate)(int) )
{
int n_bytes = 0; /* total number of bytes read or error flag */
size_t n_alloc = 0; /* number of bytes allocated */
unsigned int mult = 1;
char *tmp, *local_buffer = NULL;
int read_in;
*buf = NULL;
if( 0 == sz ) sz = LINE_LEN;
while( (read_in = fgetc( in )) != DELIMITER && read_in != EOF ) {
if( 0 == n_alloc ) {
n_alloc = sz * mult + n_bytes + 1;
tmp = realloc( local_buffer, n_alloc );
if ( NULL != tmp ) {
local_buffer = tmp;
mult++;
}
else {
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return ERROR_MEMORY;
}
}
if( NULL != validate ) {
if( 0 != validate( read_in ) ) {
local_buffer[n_bytes++] = read_in;
n_alloc--;
}
else {
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return ERROR_ILLEGAL_CHAR;
}
}
}
local_buffer[n_bytes] = '\0';
/* trim excess memory if any */
if( n_alloc > (size_t)n_bytes ) {
tmp = realloc( local_buffer, n_bytes );
if( NULL != tmp ) {
local_buffer = tmp;
}
}
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return n_bytes;
}
/* usage example */
int main( void )
{
char *line = NULL;
int ret_value;
size_t len;
fputs( "Enter a string: ", stdout );
fflush( stdout );
ret_value = fget_line( stdin, &line, 0, isalnum );
len = strlen( line );
fprintf( stdout, "fget_line() returned %d\nstrlen() returns %d bytes\n",
ret_value, len );
fprintf( stdout, "String is: \"%s\"\n", line );
free( line );
exit( EXIT_SUCCESS );
}
There was a recent thread in this group which talked about the
shortcomings of fgets(). I decided to try my hand at writing a
replacement for fgets() using fgetc() and realloc() to read a line of
arbitrary length. I know that the better programmers in this group could
write a more robust function, but here is my shot at it anyway.
I would appreciate people's comments on my fget_line() code below
(usage example included). Any constructive criticism welcome regarding
logic, design, style, etc. Thanks.
Paul
/* fget_line(): a function to read a line of input of arbitrary length.
*
* Arguments:
* 'in' -- the input stream from which data is wanted.
* 'buf' -- the address of a pointer to char. The read in results
* will be contained in this buffer after the fget_line returns.
* *** THE CALLER MUST FREE THIS POINTER ***
* 'sz' -- the caller can supply an estimate of the length of line to be
* read in. If this argument is 0, then fget_line() uses a
* default.
* 'validate' -- a user supplied callback function which is used to validate
* each input character. This argument may be NULL in which
* case no input validation is done.
*
* RETURN values:
* fget_line() on success: returns the number of bytes read
* realloc() related failure: returns -1 (#define'd below as ERROR_MEMORY)
* illegal input: returns -2 (#define'd below as ERROR_ILLEGAL_CHAR)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifndef USER_DIALOG_H /* if I am not using this as part of my
* 'user_dialog' utilities.
*/
#define LINE_LEN 80
#define DELIMITER '\n'
#define ERROR_MEMORY (-1)
#define ERROR_ILLEGAL_CHAR (-2)
#else
#include "user_dialog.h"
#endif
int fget_line( FILE *in, char **buf, size_t sz, int (*validate)(int) )
{
int n_bytes = 0; /* total number of bytes read or error flag */
size_t n_alloc = 0; /* number of bytes allocated */
unsigned int mult = 1;
char *tmp, *local_buffer = NULL;
int read_in;
*buf = NULL;
if( 0 == sz ) sz = LINE_LEN;
while( (read_in = fgetc( in )) != DELIMITER && read_in != EOF ) {
if( 0 == n_alloc ) {
n_alloc = sz * mult + n_bytes + 1;
tmp = realloc( local_buffer, n_alloc );
if ( NULL != tmp ) {
local_buffer = tmp;
mult++;
}
else {
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return ERROR_MEMORY;
}
}
if( NULL != validate ) {
if( 0 != validate( read_in ) ) {
local_buffer[n_bytes++] = read_in;
n_alloc--;
}
else {
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return ERROR_ILLEGAL_CHAR;
}
}
}
local_buffer[n_bytes] = '\0';
/* trim excess memory if any */
if( n_alloc > (size_t)n_bytes ) {
tmp = realloc( local_buffer, n_bytes );
if( NULL != tmp ) {
local_buffer = tmp;
}
}
local_buffer[n_bytes] = '\0';
*buf = local_buffer;
return n_bytes;
}
/* usage example */
int main( void )
{
char *line = NULL;
int ret_value;
size_t len;
fputs( "Enter a string: ", stdout );
fflush( stdout );
ret_value = fget_line( stdin, &line, 0, isalnum );
len = strlen( line );
fprintf( stdout, "fget_line() returned %d\nstrlen() returns %d bytes\n",
ret_value, len );
fprintf( stdout, "String is: \"%s\"\n", line );
free( line );
exit( EXIT_SUCCESS );
}