strXcpy comparison
In the beginning, there was
void.
As a C programmer you probably know strcpy(), but hopefully strncpy(), too.
But do you also know strlcpy() (from BSD) and strscpy() (from the Linux kernel)?
And do you know the suitable differences?
Summary
Calling strXcpy(dst, "text", sizeof dst) returns … and afterwards dst has … with
¶the NUL-byte·unmodified- 💥 Crash
| variant | D < S | D = S | D > S |
|---|---|---|---|
strcpy |
dest: text💥 | dest:text¶ | dest:text¶· |
strncpy |
dest: text | dest:text¶ | dest:text¶¶ |
strlcpy |
l: tex¶ | l: text¶ | l: text¶· |
strscpy |
-E2BIG:tex¶ | l: text¶ | l: text¶· |
strscpy_pad |
-E2BIG:tex¶ | l: text¶ | l: text¶¶ |
#define D 4 // or 5 or 6
char src[] = "text", dst[D];
int l = strlne(src); // 4 excluding NUL
int S = l + 1; // 5 including NUL
strXcpy(dst, src, D);
Comparison
strcpy
char *strcpy(char *dest, const char *src);
Characteristics
- Will copy unknown number of characters from
*srcto*dstuntil the first\0character is reached. - The destination
*dstmust be large enough to hold the sting. - The trailing space of the destination is not touched.
- It returns
*destunmodified. - POSIX.1-2001, POSIX.1-2008, C89, C99, SVr4, 4.3BSD.
Example
#include <stdio.h>
#include <string.h>
static const char DEFAULT[] = "text";
static char dest[sizeof DEFAULT];
int main(int argc, char **argv) {
const char *src = argc > 1 ? argv[1] : DEFAULT;
char *res = strcpy(dest, src);
printf("source length=%zd\n", strlen(src));
printf("destination length=%zd\n", strlen(dest));
printf("returned pointer=dest+%ld\n", res - dest);
printf("last character[%zd]=0x%02x\n", sizeof dest - 1, dest[sizeof dest - 1]);
return 0;
}
Compilation
gcc -Wall -o strncpy strncpy.c
strncpy
char *strncpy(char *dest, const char *src, size_t n);
Characteristics
- Will copy up to
ncharacters from*srcto*dstuntil the first\0character is reached. - If the destination
*dstis not large enough, there will be no trailing\0s. - The trailing space will be filled with
\0. - It returns
*destunmodified. - POSIX.1-2001, POSIX.1-2008, C89, C99, SVr4, 4.3BSD.
Example
#include <stdio.h>
#include <string.h>
static const char DEFAULT[] = "text";
static char dest[sizeof DEFAULT];
int main(int argc, char **argv) {
const char *src = argc > 1 ? argv[1] : DEFAULT;
char *res = strncpy(dest, src, sizeof dest);
printf("source length=%zd\n", strlen(src));
printf("destination length=%zd\n", strlen(dest));
printf("returned pointer=dest+%ld\n", res - dest);
printf("last character[%zd]=0x%02x\n", sizeof dest - 1, dest[sizeof dest - 1]);
if (dest[sizeof dest - 1]) {
printf("TRUNCATED, Manually terminating string!\n");
dest[sizeof dest - 1] = '\0';
}
return 0;
}
Compilation
gcc -Wall -o strncpy strncpy.c
strlcpy
size_t strlcpy(char *dest, const char *src, size_t size);
Characteristics
- Will copy up to
n-1characters from*srcto*dstuntil the first\0character is reached. - Will always terminate the string with one
\0character. - The trailing space of the destination is not touched.
- It returns the length of
*src. - BSD only!
Example
#include <stdio.h>
#include <string.h>
#include <bsd/string.h>
static const char DEFAULT[] = "text";
static char dest[sizeof DEFAULT];
int main(int argc, char **argv) {
const char *src = argc > 1 ? argv[1] : DEFAULT;
size_t len = strlcpy(dest, src, sizeof dest);
printf("source length=%zd\n", strlen(src));
printf("destination length=%zd\n", strlen(dest));
printf("returned len (of source)=%zd\n", len);
printf("last character[%zd]=0x%02x\n", sizeof dest - 1, dest[sizeof dest - 1]);
if (len >= sizeof dest)
printf("TRUNCATED!\n");
return 0;
}
Compilation
You need libbsd::
apt-get install libbsd-dev
gcc -Wall -o strlcpy strlcpy.c -lbsd
strscpy
ssize_t strscpy(char *dest, const char *src, size_t count);
Characteristics
- Will copy up to
n-1characters from*srcto*dstuntil the first\0character is reached. - Will always terminate the string with one
\0character (ifcount > 0). - The trailing space of the destination is not touched; for that there is
strscpy_pad(). - Returns the number of characters copied excluding the terminating
\0character or-E2BIG. - Linux kernel only!
Example
#include <stdio.h>
#include <string.h>
#include "uapi/asm-generic/errno-base.h"
static const char DEFAULT[] = "text";
static char dest[sizeof DEFAULT];
ssize_t strscpy(char *dest, const char *src, size_t count);
int main(int argc, char **argv) {
const char *src = argc > 1 ? argv[1] : DEFAULT;
size_t len = strscpy(dest, src, sizeof dest);
printf("source length=%zd\n", strlen(src));
printf("destination length=%zd\n", strlen(dest));
printf("copied=%zd\n", len);
printf("last character[%zd]=0x%02x\n", sizeof dest - 1, dest[sizeof dest - 1]);
if (len == -E2BIG)
printf("TRUNCATED!\n");
return 0;
}
/* dummy definitions */
#define __must_check
#define __noreturn
#define __malloc
#define _ASM_EXTABLE(a, b)
#define barrier_data(a)
#define pr_emerg(a, b)
#define BUG(a)
#define BUG_ON(a)
/* from include/linux/kernel.h */
#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
/* do not load Linux kernel headers */
#define _LINUX_BITOPS_H
#define _LINUX_LOG2_H
#define _LINUX_KERNEL_H
#define _ASM_X86_BUG_H
#include "../lib/ctype.c"
#include "../lib/string.c"
Compilation
You need the Linux source code::
apt-get install linux-source
gcc -Wall \
-I../linux/arch/x86/include \
-I../linux/include \
strscpy.c
Written on January 16, 2018