1
votes

I know, strlcpy is safer than strncpy while copying from source to destination character arrays where we want destination to be null terminated, Is the following wrapper ok?

size_t strlcpy(char *dst, const char *src, size_t size) {
  if (size != 0) {
    int maxSize = size - 1;
    int currSize = -1;
    while ((++currSize < maxSize) && (*dst++ = *src++));
    *dst = 0;
    return currSize;
  }
  return 0;
}

Please comment.

2

2 Answers

0
votes

The check for size to be above zero is misleading, because size_t is unsigned. A more readable check is if (size != 0) ...

Another problem is that strncpy pads its destination with zeros up to s, which strlcpy does not do. If you would like to match the behavior of strlcpy, write your own implementation on systems where it is not available, rather than relying on strncpy.

0
votes

Your implementations is very fast, but with two quirks:

  • One quirk is that your function writes two NUL-characters instead of one when there is enough space.
  • Another quirk is the return value which doesn't indicate issues.

Apart from these quircks, your version is functionally equivalent to strxcpy() by attractivechaos. However, your code is 30-100% faster depending on which machine I use. Good job in that respect!

Regarding the return value, here are the differences I observe:

  • the original strlcpy returns length of src:

    • cons: unsafe, unnecessarily slow
    • pros: strlcpy(d,s,n) is equivalent to snprintf(d,n,"%s",s)
  • strxcpy by attractivechaos returns number of bytes written:

    • pros: return value doesn't clearly indicate an issue when src is too long
    • cons: deviates from strlcpy in return value
  • your function returns length of the string length written:

    • cons:
      • return value doesn't clearly indicate an issue when src is too long
      • return value doesn't indicate whether NUL-character was written or not
    • pros: more in line with the original strlcpy.

In my preferred implementation all the mentioned functional disadvantages are fixed:

ssize_t safe_strlcpy(char *dst, const char *src, size_t size)
{
    if (size == 0)
        return -1;

    size_t ret = strnlen(src, size);
    size_t len = (ret >= size) ? size - 1 : ret;
    memcpy(dst, src, len);
    dst[len] = '\0';
    return ret;
}

It returns size when src was too long and didn't fit entirely; -1 if size is zero; otherwise - string length written. So, when everything is OK, the return value is still in line with strlcpy. And it's based on Git's implementation of the original strlcpy.