At Wed, 24 Jun 2009 20:32:35 +0200, Marc Balmer <marc%msys.ch@localhost> wrote: Subject: Re: strtonum(3) from OpenBSD? > > > Am 24.06.2009 um 20:24 schrieb Thor Lancelot Simon: > > > If it didn't set errno at all, it would be an example of really, > > really bad API design. Since it does (but doesn't do so usefully), > > it's merely an example of bad API design. > > I think this is not correcz. I think the OpenBSD strtonum(3) API is indeed a very bad design, perhaps not extremely bad, but still basically unusable even where it's authors intended to use it. (and it was extremely presumptuous of the OpenBSD folks to put it in their libc, especially in the "stdlib" category -- since it is most certainly not a standards compatible function of any kind! :-)) strtol(3) itself is perhaps not the best design, but it is at least a lot better design, and I dare say it probably had a lot more input and consideration that both OpenBSD and NetBSD developers together could bring to bear on the subject. There are inherent design conflicts in trying to come up with a good API (especially in C) which returns a value and an error indication where the value and the error indicator are both of the same basic data type and in the exact same ranges. I think OpenBSD's strtonum() solves the two returned values problem backward, at least in the context of the code and APIs common in Unix like systems, and against the design style I'm most familiar and comfortable with. The error indication should be the return value of the function so that its result can be used directly in the control flow statement which determines the next step of program execution. The data output of the function should be returned via a pass-by-reference function parameter (as may other qualifications on what kind of error occurred, if an error was indicated). This also means of course that I'm not in complete agreement about the design of strtol() and friends either -- they solve the return values conflict problem by combining two possibly valid data return values with the non-standard and irregular use of "errno" to indicate a conversion error. FWIW, here's my super-picky, super portable (except for err.h et al), version of parseint() suitable for use where hard exits on error are desired and the output range is the full INT_MIN to INT_MAX: #include <assert.h> #include <err.h> #include <errno.h> #include <limits.h> #include <stdlib.h> /* * parseint() - Using strtol() to convert a string to an "int" * * kill the executing program on error, printing an error message */ int parseint(const char *str) { char *ep; long int lval; #if (LONG_MAX == INT_MAX) /* must clear errno for this to work */ errno = 0; #endif if (str == NULL) { errx(1, "not a number -- null string"); } lval = strtol(str, &ep, 0); /* octal, decimal, or hexidecimal */ /* * assume the whole string is the number, i.e. caller must pre-trim all * trailing whitespace or any other extraneous characters. */ if (str[0] == '\0') { errx(1, "not a number -- empty string"); } if (ep == str) { assert(errno == ERANGE);/* sanity check against strtol() */ assert(lval == 0); /* sanity check against strtol() */ errx(1, "not a number -- no digits found"); } if (*ep != '\0') { errx(1, "not a number -- trailing garbage found"); } if (lval > INT_MAX #if (LONG_MAX == INT_MAX) && errno == ERANGE #endif ) { #if (LONG_MAX > INT_MAX) errno = ERANGE; #endif err(1, "maximum is %d", INT_MAX); } else if (lval < INT_MIN #if (LONG_MAX == INT_MAX) && errno == ERANGE #endif ) { #if (LONG_MAX > INT_MAX) errno = ERANGE; #endif err(1, "minimum is %d", INT_MIN); } return (int) lval; } -- Greg A. Woods Planix, Inc. <woods%planix.com@localhost> +1 416 218-0099 http://www.planix.com/
Attachment:
pgpIHmRyce5oA.pgp
Description: PGP signature