Chris Torek wrote:
CBFalconer <cb********@maineline.netwrote:
>Richard Bos wrote:
>>... strtol() is better [than atoi()] - it doesn't have undefined
behaviour on overflow.
Not quite. strtoul won't detect the out-of-range input of "-1",
for example.
This is not "out of range", by definition. By strtoul()'s
definition, admittedly. :-) If this disagrees with your own
definition, it is easy enough to prohibit a minus sign.
.... snip ...
>
Of course, strtoul() could have been defined to prohibit signs
(plus and/or minus), and callers could explicitly allow them; or
it could even have taken flags indicating how to treat signs; but
this is the situation we have now, so code like the above will
handle it.
I have written code to do just that, and input from streams (no
buffer needed). It will handle any line that a stream can supply.
I expect to modify it to use unsigned long, as that will be more
flexible eventually and the overhead is negligible. A wrapper
handles signed ints. skipwhite, unlike ignoreblanks, pushes back
the exit char. Thus ignoreblanks is static and local to this
module. The count of leading zeroes is only limited by the ability
of the stream itself. The following is an extract without
#includes.
/* -------------------------------------------------------------
* Skip to non-blank on f, and return that char. or EOF The next
* char that getc(f) will return is unknown. Local use only.
*/
static int ignoreblks(FILE *f)
{
int ch;
do {
ch = getc(f);
} while ((' ' == ch) || ('\t' == ch));
/* while (isblank(ch)); */ /* for C99 */
return ch;
} /* ignoreblks */
/*--------------------------------------------------------------
* 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)
{
return ungetc(ignoreblks(f), 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(ch));
return ungetc(ch, f);
} /* skipwhite */
/*--------------------------------------------------------------
* Read an unsigned value. Signal error for overflow or no
* valid number found. Returns 1 for error, 0 for noerror, EOF
* for EOF encountered before parsing a value.
*
* Skip all leading blanks 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, 0 result, and the next 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 '-'.
*/
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 */
ch = ignoreblks(f);
if (EOF == ch) status = EOF;
else if (isdigit(ch)) status = 0; /* digit, no error */
while (isdigit(ch)) {
digit = ch - '0';
if ((value UWARNLVL) ||
((UWARNLVL == value) && (digit UWARNDIG))) {
status = 1; /* overflow */
value -= UWARNLVL;
}
value = 10 * value + digit;
ch = getc(f);
} /* while (ch is a digit) */
*wd = value;
ungetc(ch, f);
return status;
} /* readxwd */
Not too complicated. No use of errno. Straightforward for
interactive use. Follows the conventions for wrapping of unsigned
values.
--
Chuck F (cbfalconer at maineline dot net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net>