Richard Weeks said:
For the edification a programmer (myself) with varying degrees of
skill, what exactly are strncpy's "woes?"
Let's start by clarifying exactly what strncpy does. Here is the C89 text:
Synopsis
#include <string.h>
char *strncpy(char *s1, const char *s2, size_t n);
Description
The strncpy function copies not more than n characters (characters
that follow a null character are not copied) from the array pointed to
by s2 to the array pointed to by s1 ./120/ If copying takes place
between objects that overlap, the behavior is undefined.
If the array pointed to by s2 is a string that is shorter than n
characters, null characters are appended to the copy in the array
pointed to by s1 , until n characters in all have been written.
Returns
The strncpy function returns the value of s1 .
Okay, the first thing to notice is that, along with strncmp (and, not very
interestingly, strerror), strncpy does not require any of its parameters
to represent strings. All the other str* functions declared in <string.h>
do, but strncpy and strncmp do not.
Because of this, it can reasonably be argued that they have lousy names.
Well, I agree - they do. And those are strncpy's and strncmp's woes,
basically.
The real woe lies not in the functions themselves, but in the way that they
are used. The strncpy function is often recommended, by people who ought
to know better, as a "safe" version of strcpy:
#define N 6
char target[N] = "";
strncpy(target, source, sizeof target - 1);
or perhaps
#define N 6
char target[N];
strncpy(target, source, sizeof target);
target[N - 1] = '\0';
They reason that it's better to lose some data than to invoke undefined
behaviour. Well, so it is - but just because invoking UB is wrong, that
doesn't mean that losing data is right!
That is not what strncpy is for.
It is for copying substrings, fields, call them what you like, from one
block of memory to another. The null padding is a bit of a canard, really.
Here is an example of a *good* use of strncpy (which is otherwise not
terribly well-written, but I've tried to make it as clear as possible):
char date[25] = "";
char *d = asctime(&tmstruct);
strncpy(date, d + 11, 9); /* hh:mm:ss */
strncpy(date + 9, d, 4); /* nnn */
strncpy(date + 9 + 4, d + 8, 3); /* dd */
strncpy(date + 9 + 4 + 3, d + 4, 4); /* mmm */
strncpy(date + 9 + 4 + 3 + 4, d + 20, 4); /* yyyy */
That's what it's for - building a record a piece at a time by copying data
from a record-like (multiple field) source.
And it does that job perfectly well.