Anonymous 7843 wrote:
CBFalconer <cb********@worldnet.att.net> wrote:
My attitude is that I want to know if the user is getting something
he doesn't expect.
An approach I used (a long time ago) was to do a second conversion
back into a string, then do a strcmp-like thing to see if the value
made the round-trip intact. This is pretty simple for integers,
but floating point is a bit more difficult.
Here is code I wrote some time ago to handle input from streams,
and catch all overflows.
/* ------------------------------------------------- *
* File txtinput.c *
* ------------------------------------------------- */
#include <limits.h> /* xxxx_MAX, xxxx_MIN */
#include <ctype.h> /* isdigit, isblank, isspace */
#include <stdio.h> /* FILE, getc, ungetc */
#include "txtinput.h"
#define UCHAR unsigned char
/* These stream input routines are written so that simple
* conditionals can be used:
*
* if (readxint(&myint, stdin)) {
* do_error_recovery; normally_abort_to_somewhere;
* }
* else {
* do_normal_things; usually_much_longer_than_bad_case;
* }
*
* They allow overflow detection, and permit other routines to
* detect the character that terminated a numerical field. No
* string storage is required, thus there is no limitation on
* the length of input fields. For example, a number entered
* with a string of 1000 leading zeroes will not annoy these.
*
* The numerical input routines *NEVER* absorb a terminal '\n'.
* Thus a sequence such as:
*
* err = readxint(&myint, stdin);
* flushln(stdin);
*
* will always consume complete lines.
*
* They are also re-entrant, subject to the limitations of file
* systems. e.g interrupting readxint(v, stdin) operation with
* a call to readxwd(wd, stdin) would not be well defined, if
* the same stdin is being used for both calls. If ungetc is
* interruptible the run-time system is broken.
*/
/*--------------------------------------------------------------
* Skip all blanks on f. At completion getc(f) will return
* a non-blank character, which may be \n or EOF
*
* Skipblks returns the char that getc will next return, or EOF.
*/
int skipblks(FILE *f)
{
int ch;
do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch));
/* while (isblank((UCHAR)ch)); */ /* for C99 */
return ungetc(ch, f);
} /* skipblks */
/*--------------------------------------------------------------
* Skip all whitespace on f, including \n, \f, \v, \r. At
* completion getc(f) will return a non-blank character, which
* may be EOF
*
* Skipwhite returns the char that getc will next return, or EOF.
*/
int skipwhite(FILE *f)
{
int ch;
do {
ch = getc(f);
} while (isspace((UCHAR)ch));
return ungetc(ch, f);
} /* skipwhite */
/*--------------------------------------------------------------
* Read an unsigned value. Signal error for overflow or no
* valid number found. Returns true for error, false for noerror
*
* Skip all leading whitespace on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error and the following getc returning \n
* indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: This specifically forbids a leading '+' or '-'.
* Peculiarity: This forbids overflow, unlike C unsigned usage.
* on overflow, UINT_MAX is returned.
*/
int readxwd(unsigned int *wd, FILE *f)
{
unsigned int value, digit;
int status;
int ch;
#define UWARNLVL (UINT_MAX / 10U)
#define UWARNDIG (UINT_MAX - UWARNLVL * 10U)
value = 0; /* default */
status = 1; /* default error */
do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch)); /* skipblanks */
/* while (isblank((UCHAR)ch)); */ /* for C99 */
if (!(EOF == ch)) {
if (isdigit((UCHAR)ch)) /* digit, no error */
status = 0;
while (isdigit((UCHAR)ch)) {
digit = (unsigned) (ch - '0');
if ((value < UWARNLVL) ||
((UWARNLVL == value) && (UWARNDIG >= digit)))
value = 10 * value + digit;
else { /* overflow */
status = 1;
value = UINT_MAX;
}
ch = getc(f);
} /* while (ch is a digit) */
}
*wd = value;
ungetc(ch, f);
return status;
} /* readxwd */
/*--------------------------------------------------------------
* Read a signed value. Signal error for overflow or no valid
* number found. Returns true for error, false for noerror. On
* overflow either INT_MAX or INT_MIN is returned in *val.
*
* Skip all leading whitespace on f. At completion getc(f) will
* return the character terminating the number, which may be \n
* or EOF among others. Barring EOF it will NOT be a digit. The
* combination of error and the following getc returning \n
* indicates that no numerical value was found on the line.
*
* If the user wants to skip all leading white space including
* \n, \f, \v, \r, he should first call "skipwhite(f);"
*
* Peculiarity: an isolated leading '+' or '-' NOT immediately
* followed by a digit will return error and a value of 0, when
* the next getc will return that following non-digit. This is
* caused by the single level ungetc available.
*/
int readxint(int *val, FILE *f)
{
unsigned int value;
int status, negative;
int ch;
*val = value = 0; /* default */
status = 1; /* default error */
negative = 0;
do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch)); /* skipwhite */
/* while (isblank((UCHAR)ch)); */ /* for C99 */
if (!(EOF == ch)) {
if (('+' == ch) || ('-' == ch)) {
negative = ('-' == ch);
ch = getc(f); /* absorb any sign */
}
if (isdigit((UCHAR)ch)) { /* digit, no error */
ungetc(ch, f);
status = readxwd(&value, f);
ch = getc(f); /* This terminated readxwd */
}
if (negative && (value < UINT_MAX) &&
((value - 1) <= -(1 + INT_MIN))) *val = -value;
else if (value <= INT_MAX) *val = value;
else { /* overflow */
status = 1;
if (value)
if (negative) *val = INT_MIN;
else *val = INT_MAX;
}
}
ungetc(ch, f);
return status;
} /* readxint */
/*-----------------------------------------------------
* Flush input through an end-of-line marker inclusive.
*/
int flushln(FILE *f)
{
int ch;
do {
ch = getc(f);
} while (('\n' != ch) && (EOF != ch));
return ch;
} /* flushln */
/* End of txtinput.c */
--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson