they actually started out with your design and then fixed it:
> The return values started out as the
number of characters copied, since this was trivial to
get as a side effect of the copy or concatenation. We
soon decided that a return value with the same semantics as snprintf()’s was a better choice since it gives the
programmer the most flexibility with respect to truncation detection and recovery.
basically they wanted to treat string truncation due to insufficient space as an error condition, so they designed the interface to make it easy to check (code from the paper, with syntax corrections):
len = strlcpy(path, homedir, sizeof(path));
if (len >= sizeof(path)) return (ENAMETOOLONG);
len = strlcat(path, "/", sizeof(path));
if (len >= sizeof(path)) return (ENAMETOOLONG);
your proposal does permit such simple error checking for strscpy, although it is marginally less efficient:
len = strscpy(path, homedir, sizeof(path));
if (len <= strlen(homedir)) return (ENAMETOOLONG);
but i can't think of anything your corresponding strscat could return to permit a similarly simple check. is there anything?
Basically, it seems like they wanted to facilitate the use case of "if the string is truncated, then realloc() the buffer to make it fit using the value convienently returned from strlcpy()".
(note: the following code is probably buggy with off by one errors and doesn't check return value properly)
I can appreciate that sentiment, but I think it was a mistake. That behavior is still possible where it makes sense by using strlen() first, but I'd argue that in most cases if this is possible then strdup() was the better solution all along. Basically they made a tradeoff that makes one relatively uncommon use case easier at the expense of making the function explode in other cases.
You don't need strlen() to check for truncation:
if ( strscpy(path, homedir, sizeof(path) == sizeof(path) ) return (ENAMETOOLONG);
strscat() would have a similar syntax. Return values would be the same, including returning the size parameter in the case of truncation, making it easy to check.
if ( strscat(path, "/", sizeof(path) == sizeof(path) ) return (ENAMETOOLONG);
On a side note I always cringe when I see people using sizeof() on strings in C. I left them in here to make the comparison easier, but I wouldn't normally do it this way. That's a gun pointed directly at your foot when this bit of code gets refactored out to a function and that string degrades to a pointer.
i think they wanted to facilitate the use case of 'if the string is truncated, then close the connection and log an error message', or 'if the string is truncated, then return an error code', as in the example code i quoted from the paper
strdup() is not helpful in examples like the one i quoted from the paper, where you are building up a string by concatenating substrings, but something like stralloc is. (see other subthread) the paper recommends the libmib astring functions, which are something like stralloc: http://www.mibsoftware.com/libmib/astring/. they definitely were not recommending that people copy and paste those six lines of code with slight changes every time they wanted to copy a string
i don't agree that it makes the function explode in other use cases. if you're okay with truncation then strlcpy() will silently truncate your strings if you don't check its return value
your strscpy() example has a parse error; i think you meant
if ( strscpy(path, homedir, sizeof(path)) == sizeof(path) ) return (ENAMETOOLONG);
which leads me to think that you mean that if strlen(homedir) is 12 and sizeof(path) is 13, strscpy copies 12 characters (not counting the nul) and returns 12, not 13, while if strlen(homedir) is 13 in that case, it also copies 12 characters, but returns 13. i agree that that would work; it is so similar to the flawed design rejected in the strlcpy paper that i thought you meant the same thing, but you evidently meant something subtly different. i agree that that design would also work for strscat
at that point, though, it might be better to return -1 or INT_MAX rather than dsize on truncation; you can't use the return value you've specified for anything before you check whether it's equal to dsize or not. (this is also true of strlcpy!) actually you also specified to return a negative value on certain other errors, which means you have to check the return value twice before using it for anything; possibly this was a mistake
i also agree that using sizeof on arrays is a footgun for exactly the reason you say, although in this case the most likely result would be that you'd notice the bug and fix it, since pointers are too short for most strings
As discussed a few weeks ago, strlen() + memcpy() is faster than strlcpy() on superscalar platforms with branch prediction. Iterating over the string twice is not a penalty if the alternative hobbles the hardware with more complex code.
they actually started out with your design and then fixed it:
> The return values started out as the number of characters copied, since this was trivial to get as a side effect of the copy or concatenation. We soon decided that a return value with the same semantics as snprintf()’s was a better choice since it gives the programmer the most flexibility with respect to truncation detection and recovery.
basically they wanted to treat string truncation due to insufficient space as an error condition, so they designed the interface to make it easy to check (code from the paper, with syntax corrections):
your proposal does permit such simple error checking for strscpy, although it is marginally less efficient: but i can't think of anything your corresponding strscat could return to permit a similarly simple check. is there anything?