[HN Gopher] Ratatoi is a C libary that wraps stdlib's strtol (as...
       ___________________________________________________________________
        
       Ratatoi is a C libary that wraps stdlib's strtol (as atoi does),
       but it's evil.
        
       Author : rept0id-2
       Score  : 24 points
       Date   : 2025-05-21 19:00 UTC (4 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | rept0id-2 wrote:
       | Ratatoi is a C libary that wraps stdlib's strtol (as atoi does),
       | but it's evil.
       | 
       | If an overflow is detected, it calls abort(), you crash and get
       | Aborted (core dumped).
       | 
       | This way, you prioritize memory safety over silently running in a
       | wrong state, without needing to call strtol and check errors
       | manually everywhere.
        
       | timewizard wrote:
       | "This way, you open yourself up to DDoS attacks, instead of just
       | handling your own errors correctly."
        
         | kstrauser wrote:
         | I prefer this approach. It's kind of like using `.expect()` or
         | `.unwrap()` in Rust when there's no plausible way the call
         | should fail. Like if my program writes out a JSON file and then
         | reads it back in, and the file isn't value JSON, panicking is a
         | reasonable way to deal with the situation. Someone the world
         | got itself into a strange state I can't reasonably recover
         | from.
         | 
         | Well, same here. If you're using strtol on data that must be
         | well-formed, and it's not, and you're at an earlier stage of
         | startup like parsing the config file, go ahead and blow up.
         | That's almost certainly better than plowing ahead with invalid
         | data.
        
           | LukeShu wrote:
           | > Like if my program writes out a JSON file and then reads it
           | back in, and the file isn't value JSON, panicking is a
           | reasonable way to deal with the situation.
           | 
           | Like how if I hit ctrl-C at just the right moment during the
           | build, the next time I build, cmake will segfault and I have
           | to delete the build directory.
        
       | threeducks wrote:
       | I think strtol is just a badly designed function. The return
       | value should have been an error code and the actual long should
       | have been "returned" via a pointer. Checking the return value is
       | much easier than checking endptr and errno and remembering to set
       | errno before calling strtol.
       | 
       | The fact that the strtol example in the manual is 50 lines long,
       | of which most is error handling, speaks for itself.
       | https://man7.org/linux/man-pages/man3/strtol.3.html#:~:text=...
       | 
       | That being said, I can't imagine many applications where crashing
       | is a good solution.
        
         | wahern wrote:
         | OpenBSD added strtonum to <stdlib.h>. It's quite opinionated,
         | but fits the usage patterns OpenBSD developers prefer.
         | 
         | > 50 lines long, of which most is error handling
         | 
         | That's a little exaggerated considering the example is an
         | entire C program, including main. It's more like 14 lines,
         | ignoring the '\0' check (which isn't necessary if you don't
         | want to parse additional items), and even that includes
         | whitespace and stderr logging.
         | 
         | I agree the biggest headache is reliance on errno and the
         | nuanced--albeit conventional, consistent[1], and documented--
         | semantics of only setting errno on error. Some POSIX APIs, at
         | least those defined from scratch (e.g. pthreads, as opposed to
         | incorporated common extensions) prefer returning the error code
         | directly as you recommend. But this would technically only save
         | you a single line, though it might save alot of confusion about
         | semantics.
         | 
         | However, in this case I might keep returning the value directly
         | and take an error pointer, similar to OpenBSD's strtonum.
         | Otherwise you would need separate routines for char, short,
         | int, long, and long long. Though there's still the issue of
         | unsigned integers, and using _Generic it might be possible to
         | at least hide all the type-specific variants behind a single
         | interface.
         | 
         | And there's still the issue of needing to check for trailing
         | garbage, or dropping the ability to use the interface inline
         | with other parsing code. There's alot of dimensions to the
         | problem. Parsing integers may be conceptually simple[2], but
         | designing an interface that's easy to _integrate_ into
         | applications across a variety of contexts is much less simple.
         | 
         | [1] Consistent across libc interfaces, excepting those that may
         | invoke syscalls internally, like printf, where errno may be
         | incidentally modified even on success.
         | 
         | [2] I personally often prefer to just write a simple loop to
         | manually parse integers. It's sometimes easier to integrate the
         | desired error checking inline, or even elide it altogether
         | (garbage-in, garbage-out).
        
         | gpderetta wrote:
         | C can actually return structs by value and small structs are
         | actually handled quite efficiently in some ABIs, so a pair
         | result/error would be quite convenient although I guess not
         | idiomatic.
        
         | PaulDavisThe1st wrote:
         | > The return value should have been an error code and the
         | actual long should have been "returned" via a pointer.
         | 
         | Oh, you mean like:                   int ret = sscanf (str,
         | "%d", &value);          ?
        
           | sltkr wrote:
           | Yes, that API is actually great, but the problem with
           | (s)scanf() is that reading an invalid value is undefined
           | behavior. So you can't use it if you don't already know the
           | result fits in &value, which is exactly the situation where
           | you'd use strtol() instead.
        
         | duneroadrunner wrote:
         | So I don't write much C code these days, but I recently
         | encountered strtol() again and am I mistaken or does the
         | interface also violate const correctness? I mean it takes a
         | _const char*_ as the first parameter and then gives you back a
         | (non- _const_ ) _char*_ potentially pointing into the same
         | string, right? Like, does strtol() get a pass because it 's
         | old, or is const correctness (still) not generally a concern of
         | C programmers?
        
       | ksherlock wrote:
       | Your errno checks aren't correct.
       | 
       | errno is only set on an error. It's not cleared on success. If
       | errno was previously set, the function will always abort(). So
       | you need to do something like:                   int saved_errno
       | = errno;         errno = 0;         ....         errno =
       | saved_errno;         return aInt;
        
         | ben0x539 wrote:
         | Doesn't it set errno to 0 first thing?
        
           | ksherlock wrote:
           | ahh, you're right. It's still polite to save and restore
           | though :)
        
       | snarfy wrote:
       | pun driven development
        
       | cmovq wrote:
       | Interestingly, a complete implementation of strtol [1] is shorter
       | than this wrapper. If you don't like strtol's API or error
       | handling, just implement your own.
       | 
       | [1]: https://github.com/gcc-
       | mirror/gcc/blob/master/libiberty/strt...
       | 
       | > If an overflow is detected, it calls abort()
       | 
       | An aside, but this doesn't detect overflows on Windows due to
       | both long and int being 32 bits (you'd want strtoll for that).
        
       ___________________________________________________________________
       (page generated 2025-05-21 23:01 UTC)