In article <
0b30b0cc-f7c5-4928...@googlegroups.com>,
This has been covered, but I'll throw my hat in the ring for the,
"neither" camp.
The biggest deficiencies of strncpy() have to do with its surprising
semantics: it was originally written for copying data into
fixed-width fields of data structures that were written to disk
(actually as part of a filesystem). Thus, it zeros out the part of
the destination after the end of the source if the source is shorter
than the buffer size (instead of just appending a single NUL
terminator), and possibly won't NUL terminate if the source is of
length equal-to or longer than the size argument. It always writes
exactly 'size' characters, which is inefficient for short source
strings and large destination buffers.
The possible lack of NUL termination requires care on the part of the
programmer and is sufficiently non-intuitive that it is often
overlooked: this has been a fruitful source of errors over the years.
Further, there's no way to detect truncation (short of comparison
against the source string after the copy or similar).
snprintf() doesn't have the NUL-termination or zeroing problem, but
requires a lot of machinery (all the rest of printf, which is
complicated in its full glory) and is inefficient. However,
snprintf() *does* return a useful value that can be used to probe
for truncation.
The,
*dst = 0;
strncat(dst, src, size - 1);
trick is clever and reasonably efficient, but leaves the programmer
no way to detect truncation (again, unless followed by a comparison
against the source or something).
It should be noted that this "compare against the source" business as
a workaround to detect truncation is redundant: the copying routine
could report if it truncates or not, so requiring a second probing
step should not be necessary. Perhaps that's fine for one's
particular application, but the general problem persists.
To summarize, no string-copying function in the C standard library
provides an acceptable combination of efficiency, safety and the
ability to detect truncation.
The OpenBSD people recognized this some years ago and came up with
functions that had the right properties: strlcpy() and strlcat().
These are pretty simple. They will *always* terminate a greater-than-
zero length destination buffer, they will never write more than one
terminating NUL character, and they return a value that can be used
to detect truncation. They are available from the OpenBSD
distribution under a permissive license and have made it into most
of the BSD distributions, the Mac, and (I think) Solaris. Most Linux
distributions provide copies in a BSD compatibility library, though
they were rejected from glibc because, well, Ulrich Drepper didn't
like them.
An ersatz implementation of strlcpy() taken from
http://pub.gajendra.net/src/strlcpy.c is:
#include <stddef.h>
#include <string.h>
size_t
strlcpy(char *dst, const char *src, size_t size)
{
size_t len, srclen;
/*
* Get the length of the source first, test for the
* pathological case, then copy as much as we can.
*/
srclen = strlen(src);
if (size-- == 0)
return(srclen);
len = (size < srclen) ? size : srclen;
memmove(dst, src, len);
dst[len] = '\0';
return(srclen);
}
Similarly, an implementation of strlcat() taken from
http://pub.gajendra.net/src/strlcat.c is:
#include <stddef.h>
#include <string.h>
size_t
strlcat(char *dst, const char *src, size_t size)
{
char *p;
size_t len;
/*
* This mimics the OpenBSD semantics most closely.
*
* We would like to use strlen() here, but the idea is to
* catch cases where dst isn't a C-style string, and this
* function is (presumably) called in error.
*/
p = memchr(dst, '\0', size);
if (p == NULL)
p = dst + size;
len = p - dst;
return(len + strlcpy(p, src, size - len));
}
Feel free to use these, if you'd like (I'm the original author of
these versions and have put them into the public domain).
- Dan C.