Making Strlcpy Available in Linux

Making strlcpy available in linux

The best solution to port BSD applications is libbsd; and it's already packaged for most systems.

On Debian-based systems the development package is named libbsd-dev.

You can compile unmodified BSD source code by adding the following to your CFLAGS:
-DLIBBSD_OVERLAY -I/usr/include/bsd, and linking with -lbsd.

However, instead of hardcoding those values, you should use pkg-config with the libbsd-overlay package if you intend to distribute your build system.

unable to test the non standard function strlcpy

When I try to compile my code with gcc -Wall -Wextra -Werror
ft_strlcpy.c, I get this:

/usr/bin/ld: /tmp/ccaTaHki.o: in function `test':
ft_strlcpy.c:(.text+0x59): undefined reference to `strlcpy'
collect2: error: ld returned 1 exit status

That is a linker error, indicating that the function you are trying to call is not found in your code or in any of the libraries being linked, including the C standard library.

Having recognized that, the (Linux) manual page for strlcpy() quickly yields a probable reason and solution where it says, near the top:

Library

Utility functions from BSD systems (libbsd, -lbsd)

Supposing that that documentation is relevant to you, which it probably is if you are compiling with gcc, it tells you not only what library but exactly what link option to use: -lbsd. Therefore, compile your code this way:

gcc -Wall -Wextra -Werror ft_strlcpy.c -lbsd

That works for me.

Undefined reference to strlcpy and strlcat

As is (sort of) explained in the libbsd man page, you need to link the libbsd library as well as including the header. So add -lbsd to your command line when linking. For a simple program, you might do

gcc -o prog prog.c -lbsd

Note that the ordering of options is important here, see Why does the order in which libraries are linked sometimes cause errors in GCC? If you put -lbsd before your source file on the command line, it will probably not work.

The way you asked the question suggests you might have some confusion about the difference between a header and a library, and the roles that each one plays. You may want to read What's the difference between a header file and a library?

(This is almost a duplicate of when I use strlcpy function in c the compilor give me an error, but that question is more generic and some of the answers aren't applicable to Ubuntu specifically, so I thought a separate answer would be useful.)

my version of strlcpy

Although you could simply use another strlcpy function as another post recommends, or use snprintf(dest, len, "%s", src) (which always terminates the buffer), here are the things I noticed looking at your code:

size_t s_strlcpy(char *dest, const char *src, const size_t len)
{
size_t i = 0;

No need to make len const here, but it can be helpful since it checks to make sure you didn't modify it.

    /* Always copy 1 less then the destination to make room for the nul */
for(i = 0; i < len - 1; i++)
{

Oops. What if len is 0? size_t is usually unsigned, so (size_t)0 - 1 will end up becoming something like 4294967295, causing your routine to careen through your program's memory and crash into an unmapped page.

        /* only copy up to the first nul is reached */
if(*src != '\0') {
*dest++ = *src++;
}
else {
break;
}
}

/* nul terminate the string */
*dest = '\0';

The above code looks fine to me.

    /* Return the number of bytes copied */
return i;
}

According to Wikipedia, strlcpy returns strlen(src) (the actual length of the string), not the number of bytes copied. Hence, you need to keep counting the characters in src until you hit '\0', even if it exceeds len.

Also, if your for loop terminates on the len - 1 condition, your function will return len-1, not len like you'd expect it to.


When I write functions like this, I usually prefer to use a start pointer (call it S) and end pointer (call it E). S points to the first character, while E points to one character after the last character (which makes it so E - S is the length of the string). Although this technique may seem ugly and obscure, I've found it to be fairly robust.

Here's an over-commented version of how I would write strlcpy:

size_t s_strlcpy(char *dest, const char *src, size_t len)
{
char *d = dest;
char *e = dest + len; /* end of destination buffer */
const char *s = src;

/* Insert characters into the destination buffer
until we reach the end of the source string
or the end of the destination buffer, whichever
comes first. */
while (*s != '\0' && d < e)
*d++ = *s++;

/* Terminate the destination buffer, being wary of the fact
that len might be zero. */
if (d < e) // If the destination buffer still has room.
*d = 0;
else if (len > 0) // We ran out of room, so zero out the last char
// (if the destination buffer has any items at all).
d[-1] = 0;

/* Advance to the end of the source string. */
while (*s != '\0')
s++;

/* Return the number of characters
between *src and *s,
including *src but not including *s .
This is the length of the source string. */
return s - src;
}

when I use strlcpy function in c the compilor give me an error

undefined reference to `strlcpy'

This happens when the linker (collect2 if you are using gcc) can not find the definition of the function it complains about (not the declaration or prototype, but the definition, where the function's code is defined).

In your case it may happen because there is no shared object or library with strlcpy's code to link against. If you are sure there is a library with the code and you want to link against it, consider specifying the path to the library with the -L<path_to_library> parameter passed to the compiler.

Safe String Functions In Mac OS X and Linux

There are two strategies for safe string manipulation. The Linux / glibc maintainers refuse to add safe functions, arguing that you should keep the length of your strings at hand and use memcpy.

On the other hand, Mac OSX includes strlcpy and strlcat from BSD. snprintf and asprintf can be used on both platforms to much the same effect:

size_t strlcpy(char *d, char const *s, size_t n)
{
return snprintf(d, n, "%s", s);
}

size_t strlcat(char *d, char const *s, size_t n)
{
return snprintf(d, n, "%s%s", d, s);
}

You could also consider using the BSD implementation found here. If your code will be compiled on multiple platforms, you can test for the presence of glibc using pre-defined library macros:

#if defined __GNU_LIBRARY__ || defined __GLIBC__

size_t strlcpy(char *, char const *, size_t);
size_t strlcat(char *, char const *, size_t);

#endif

Conversion between character encodings is most easily handled using the iconv interface.

Implement of original strlcpy function on Libc

There is no reason to compute the length of the string in dst for strlcpy: dst_len = strlen(dst); is useless and counterproductive.

Here is a modified version:

size_t  ft_strlcpy(char *dst, const char *src, size_t dstsize)
{
size_t i;

i = 0;
while (i + 1 < dstsize && src[i] != '\0') {
dst[i] = src[i];
i++;
}
if (i < dstsize) {
dst[i] = '\0';
}
while (src[i] != '\0') {
i++;
}
return i;
}

Regarding your question:

how to print trace trap error when dstsize is a negative number?(I know it will be converted to size_t max number.)

If the destination size passed by the caller is a negative number, ie: the result of some computation that produces or would produce a negative number using signed arithmetics, it is converted to size_t modulo SIZE_MAX + 1, hence the value is huge.

You can detect this by comparison:

 if (dstsize > SIZE_MAX >> 1) {
fprintf(stderr, "ft_strlcpy: huge dstsize indicates a negative value was passed: %zd\n", dstsize);
abort();
}


Related Topics



Leave a reply



Submit