tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: strtonum(3) from OpenBSD?




Am 24.06.2009 um 22:24 schrieb Greg A. Woods:

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;
}


what I find amusing is that people come up with their own implemetation
of such a function instead of simply accept the fact that there is a function
in OpenBSD and FreeBSD that is accepted by many good folks...

strtonum(3) may not be perfect, indeed it has some issues when it comes
to an internationalized OS.   But why be special and not have a function
that other BSDs have as well?  It might indeed be bold to put it in
stdlib, but then, there are other places like libutil.

I did never anticipate the strong reactions I got from some people to
my simple question why it was not there - and I must say I got the
strongest reactions from the most uninformed people that had no
clue about the function....  I hope this is not because of my backgound
or the background of the function.  I try to be really neutral in this
regard ;)

Personally I find

- it makes no sense to create a NetBSD specific equivalent of
  a function that other BSDs have
- It would ease porting software between the BSDs if we had
  this function somewhere

That said I leave the issue as is, crawl back under my rock and
continue to work on my gpio(4) work ;)

Nevertheless, I enjoy a lot being here.

- Marc


--
                                                Greg A. Woods
                                                Planix, Inc.

<woods%planix.com@localhost>       +1 416 218-0099        http://www.planix.com/



Home | Main Index | Thread Index | Old Index