/******************************************************************************* * mm_input.c - Copyright (c) 2014 www.mahonri.info. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. *------------------------------------------------------------------------------ * @file mm_input.c * @author www.mahonri.info * @brief C stream input related functions. */ /******************************************************************************* * Compiler setup. */ #include // isspace() #include // errno, ENOMEM #include // malloc(), realloc(), free() #include // strchr() #include #include /******************************************************************************* * @brief Populate a dynamically allocated string with text from a specified stream. * @param I__stream FILE pointer for the input stream provided by the caller. * (Caller may specify 'stdin' or any other valid FILE * stream.) * @param _O_string Returns the string read from I__stream in allocated memory. * NULL may be specified if the string is not needed by the caller. * @param I__delimiters A string of delimiters that when any one of them is encountered * in the stream, will cause the function to return. NULL may * be specified if no delimiters will be used. If NULL is * specified, function will return when the stream EOF condition * is encountered. ( can simulate EOF from a keyboard). * @param I__validChars A string which specifies a set of acceptable characters. * NULL may be specified if no character filter is desired. * @return 0 or error code, including: * EINVAL The caller-specified stream is NULL. * ENOMEM Insufficient memory for operation. */ int MM_InputText( FILE *I__stream, char **_O_string, const char *I__delimiters, const char *I__validChars ) { int rCode = MM_SUCCESS; int unfinished = MM_TRUE; /** Validate the user-specified stream. **/ if(NULL == I__stream) { rCode=EINVAL; goto CLEANUP; } /** The length of the string in 'buf' (not counting the '\0' terminator) **/ size_t bufLen = 0; /** The size of the memory allocated to 'buf' **/ size_t bufSize = 0; /** Buffer where input will accumulate **/ char *buf = NULL; /** Initial buffer to hold string termination character '\0'. **/ errno=0; buf=malloc(bufSize+1); if(NULL == buf) { rCode=errno?errno:ENOMEM; goto CLEANUP; } buf[bufSize++] = '\0'; /** Loop getting characters to build buf. **/ while(unfinished) { int ch; /* Get a character from the stream. */ errno=0; ch=fgetc(I__stream); /* EOF handler. */ if(EOF == ch) { if(feof(I__stream)) break; rCode=errno?errno:EIO; goto CLEANUP; } /* Handle delimiter set. */ if(I__delimiters && strchr(I__delimiters, ch)) break; /* Validate character. */ if(I__validChars && (NULL == strchr(I__validChars, ch))) continue; /* Does buf need to grow larger? */ if(bufSize <= bufLen + 1) { char *x; errno=0; x=realloc(buf, ++bufSize); if(NULL == x) { rCode=errno?errno:ENOMEM; goto CLEANUP; } buf=x; } /* Store the character in the array and re-terminate the string. */ buf[bufLen++] = ch; buf[bufLen] = '\0'; } /** Return allocated buffer to caller. (if caller desires it.) **/ if(_O_string) { *_O_string = buf; buf = NULL; } CLEANUP: /** Free the buffer in an error condition, or if caller doesn't want it. */ if(buf) free(buf); return(rCode); } /******************************************************************************* * @brief Populate a dynamically allocated string with a line from a specified stream. * @param I__stream FILE pointer for the input stream provided by the caller. * (Caller may specify 'stdin' or any other valid FILE * stream.) * @param _O_string Returns the string read from I__stream in allocated memory. * NULL may be specified if the string is not needed by the caller. * @return 0 or error code, including: * EINVAL The caller-specified stream is NULL. * ENOMEM Insufficient memory for operation. */ int MM_InputLine( FILE *I__stream, char **_O_string ) { return(MM_InputText(I__stream, _O_string, "\n", NULL)); } /******************************************************************************* * @brief Reads an unsigned int from the specified stream, delimited by a newline, space or comma. * @param I__stream FILE pointer for the input stream provided by the caller. * (Caller may specify 'stdin' or any other valid FILE * stream.) * @param _O_uint Returns the unsigned integer read from I__stream. * NULL may be specified if the number is not needed by the caller. * @return 0 or error code, including: * EINVAL The caller-specified stream is NULL. * ENOMEM Insufficient memory for operation. */ int MM_InputTextUnsingedInt( FILE *I__stream, unsigned int *_O_uint ) { int rCode = MM_SUCCESS; char *uintStr = NULL; rCode=MM_InputText(I__stream, &uintStr, "\n,", "\t 0123456789"); if(rCode) goto CLEANUP; if(_O_uint) { char *cp = uintStr; while(isspace(*cp)) ++cp; *_O_uint = strtoul(cp, NULL, 10); } CLEANUP: if(uintStr) free(uintStr); return(rCode); }