bin/142911: [patch] vmstat(8) -w should produce error message
if fed a negative value
Bruce Evans
brde at optusnet.com.au
Thu Jan 21 08:10:08 UTC 2010
The following reply was made to PR bin/142911; it has been noted by GNATS.
From: Bruce Evans <brde at optusnet.com.au>
To: Efstratios Karatzas <gpf.kira at gmail.com>
Cc: bug-followup at FreeBSD.org, Bruce Evans <brde at optusnet.com.au>
Subject: Re: bin/142911: [patch] vmstat(8) -w should produce error message
if fed a negative value
Date: Thu, 21 Jan 2010 19:07:35 +1100 (EST)
On Wed, 20 Jan 2010, Efstratios Karatzas wrote:
> Thanks for the input Bruce.
>
> I studied the manpages of the aforementioned alternatives to atoi and strto*.
> I also examined code from OpenBSD and NetBSD to see how they deal with
> this kind of problem. My conclusion is that strtonum() is the best choise
> when dealing with simple argument parsing, ...
> correctly and the error checking is kinda ugly.
> The only thing I don't like about this fix is the self-imposed limit on the
> values we accept.
> min limit for everything is 0
> -w max is an hour
> -c max is 10 000
> -n max is 1000
I don't like arbitrary limits. I would use INT_MAX, etc.
% --- vmstat.c 2010-01-20 17:12:09.000000000 +0200
% +++ vmstat.orig.c 2010-01-20 16:58:56.000000000 +0200
The patch is reversed. This can be confusing.
% @@ -196,9 +195,7 @@
% aflag++;
% break;
% case 'c':
% - reps = (int)strtonum(optarg, 0, 10000, &errstr);
I don't like casting the return value, just to silence broken compilers/
CFLAGS. strtonum() is supposed to be easy to use, but it is not so
easy to use if you have to put unnecessary type info in or near it.
If I wanted to do that then I would want an interface with the type
in the name, like the one that this is replacing (atoi() for ints, and
strtol() for longs, but unfortunately no strtoi() for ints).
% - if (errstr != NULL)
% - errx(1, "-c %s: %s", optarg, errstr);
The previous line has lots of trailing white space which looks like an
extra blank line after line wrap.
% @@ -226,9 +223,10 @@
% break;
% case 'n':
% nflag = 1;
% - maxshowdevs = (int)strtonum(optarg, 0, 1000, &errstr);
% - if (errstr != NULL)
% - errx(1, "-n %s: %s", optarg, errstr);
% + maxshowdevs = atoi(optarg);
% + if (maxshowdevs < 0)
% + errx(1, "number of devices %d is < 0",
% + maxshowdevs);
Your error messages are a bit too cryptic. The old one here is better.
Old: "vmstat: number of devices -1 is < 0"
New: "vmstat: -n -1: too small"
Not too bad, but it is better to print the limit that was not met. Only
strtonum() is well placed to do this, but it only prints a generic message.
% break;
% case 'p':
% if (devstat_buildmatch(optarg, &matches, &num_matches) != 0)
% @@ -302,14 +298,9 @@
% #define BACKWARD_COMPATIBILITY
% #ifdef BACKWARD_COMPATIBILITY
% if (*argv) {
% - interval = (unsigned int)strtonum(*argv, 0, 3600, &errstr);
% - if (errstr != NULL)
% - errx(1, "wait time %s: %s", *argv, errstr);
% - if (*++argv) {
% - reps = (int)strtonum(*argv, 0, 10000, &errstr);
% - if (errstr != NULL)
% - errx(1, "iterations %s: %s", *argv, errstr);
% - }
% + interval = atoi(*argv);
% + if (*++argv)
% + reps = atoi(*argv);
% }
% #endif
%
strtonum() is still painful to use after adding error handling, sigh.
The above messages are less cryptic than the correspnding ones for -w
and -n. I prefer them for -w and -n too.
Concerning strtonum()'s bad API, I prefer something like:
uintmax_t strtoix(const char restrict *nptr, intmax_t min, uintmax_t max,
const char restrict *errfmt, ...);
so that the above can become:
interval = strtoix(*argv, 0, 3600, "wait time %s: %m", *argv);
if (*++argv)
reps = strtoix(*argv, 0, 10000, "iterations %s: %m",
*argv);
, plus many variants to regain control over standard features of the
strto*() family: (char **endptr, int base, long double fmax, int
typectl, long double fmin, long double *fresultptr, int errctl) and
add extensions (int multiplier_ctl, int unit_ctl, ...). A version
that can handle either integers or floating point in input, and output
both (e.g., decimal integer <1 followed by 100 zeros> -> UINTMAX_MAX/ERANGE
together with 1e100/no_fp_err) necessarily takes a lot of parameters,
but this is still easier to use than building a general number parser
out of strtouimax() and strtold().
Concerning implementation errors in strtonum(): it does undocumented
clobbering of errno: it sets errno to 0 even when there is no error.
This particular clobbering is explicitly forbidden for any Standard C
Library function.
Bruce
More information about the freebsd-bugs
mailing list