I apologize for not understanding your constrains more fully. After checking:
$ man strlcat
No manual entry for strlcat
I thought you were making a new strlcat
up and prepared one accordingly that would self allocate, etc. I have now found a man page for strlcat
and can now offer a bit more help on how a strlcat
could be written
strlcat
concatenates both strings taking the maximum size of the dst
buffer as the third argument (size meaning it must include space for the terminating nul-character as opposed to the length of both strings) strlcat
will append at most size - strlen(dst) - 1 bytes
, NUL-terminating the result. strlcat
returns the total length of the string it tried to create.
With those constraints, you could write the strlcat
function something like the following (using string indexes rather than pointers since that may be a bit more familiar to you)
size_t strlcat (char *dst, const char *src, size_t size)
{
/* dst length and general variable i */
size_t dlen = strlen (dst), i;
if (!src || !*src) /* validate src not NULL or empty */
return dlen;
/* concatenate at most size - 1 bytes & nul-terminate */
for (i = 0; i < size - dlen - 1 && src[i]; i++)
dst[i + dlen] = src[i];
dst[i + dlen] = 0;
return i + dlen; /* dest + bytes of src concatenated */
}
Putting it together in a short example, you could do something like the following:
#include <stdio.h>
#include <string.h>
#define MAXS 32
size_t strlcat (char *dst, const char *src, size_t size)
{
/* dst length and general variable i */
size_t dlen = strlen (dst), i;
if (!src || !*src) /* validate src not NULL or empty */
return dlen;
/* concatenate at most size - 1 bytes & nul-terminate */
for (i = 0; i < size - dlen - 1 && src[i]; i++)
dst[i + dlen] = src[i];
dst[i + dlen] = 0;
return i + dlen; /* dest + bytes of src concatenated */
}
int main (int argc, char **argv) {
size_t n = 0, sz1 = 0; /* length variables, total and 1st string */
char str[MAXS] = "",
*s1 = argc > 1 ? argv[1] : "hello", /* string 1 */
*s2 = argc > 2 ? argv[2] : "world"; /* string 2 */
printf ("\nconcatenating empty src with\ns1: %s\ns2: %s\n\n", s1, s2);
sz1 = strlcat (str, s1, MAXS); /* concatenate s1 -> str */
printf ("after 'strlcat (str, s1, n)', str : %s (%zu chars)\n", str, sz1);
n = strlcat (str, s2, MAXS); /* concatenate s2 -> str */
printf ("after 'strlcat (str, s2, n)', str : %s (%zu chars)\n", str, n);
return 0;
}
Example Use/Output
$ ./bin/strlcat
concatenating empty src with
s1: hello
s2: world
after 'strlcat (str, s1, n)', str : hello (5 chars)
after 'strlcat (str, s2, n)', str : helloworld (10 chars)
$ ./bin/strlcat strlcat _works
concatenating empty src with
s1: strlcat
s2: _works
after 'strlcat (str, s1, n)', str : strlcat (7 chars)
after 'strlcat (str, s2, n)', str : strlcat_works (13 chars)
Testing MAXS length restriction (attempting to concatenate a total of 33-chars (2-more than allowed):
$ ./bin/strlcat 12345678901234567890 1234567890123
concatenating emptr sry with
s1: 12345678901234567890
s2: 1234567890123
after 'strlcat (str, s1, n)', str : 12345678901234567890 (20 chars)
after 'strlcat (str, s2, n)', str : 1234567890123456789012345678901 (31 chars)
Custom Self Allocating Version (before finding man strlcat
)
There is learning to be had in this version as well, so I will leave it as originally posted.
It is hard to tell what all your constraints are as you are writing a strcat
function, but using memcpy
. Regardless, the following code provides a working strlcat
in my best understanding of what you are trying to achieve.
As you have hinted at in your code, there are two cases to cover (1) where dst
is empty where you essentially just allocate for dst
and copy src -> dst
and then (2) where both src
and dst
contain strings and you need to concatenate the strings (presumably placing a space
in between the words). You are free to use your own my_strlen
function in your code, I simply use strlen
below as that didn't appear to be an area that was the source of your problem.
Putting the pieces together, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strlcat (char **dst, const char *src, size_t *n)
{
size_t dlen, slen; /* dst & src lengths */
void *tmp = NULL; /* realloc variable */
if (!src) /* validate src not NULL */
return *dst;
if (!*dst) /* realloc dst (1) if empty; (2) extending current length */
tmp = realloc (*dst, (dlen = 0) + (slen = strlen (src)) + 2);
else
tmp = realloc (*dst, (dlen = strlen (*dst)) + (slen = strlen (src)) + 2);
if (!tmp) /* validate realloc */
return *dst;
*dst = tmp; /* assign reallocated block to dst */
if (!dlen) { /* if no *dst, just copy src to *dst */
strcpy (*dst, src);
*n = slen; /* update total length */
}
else { /* if *dst non-empty */
(*dst)[dlen] = ' '; /* add ' ' between *src/dst */
strcpy (*dst + dlen + 1, src); /* copy src to end (after ' ') */
*n = dlen + slen + 1; /* update total length */
}
return *dst; /* return pointer to new string */
}
int main (int argc, char **argv) {
size_t n = 0; /* length variaale */
char *s1 = argc > 1 ? argv[1] : "hello", /* string 1 */
*s2 = argc > 2 ? argv[2] : "world", /* string 2 */
*str = NULL; /* string to hold concatenated result */
strlcat (&str, s1, &n); /* concatenate s1 -> str */
strlcat (&str, s2, &n); /* concatenate s2 -> str */
printf ("src: %s\ndst: %s\nstr: %s\n", s1, s2, str);
printf ("len: %zu chars.\n", n);
free (str); /* don't forget to free allocated memory */
return 0;
}
(note: I pass the address of dst
to strlcat
which will allow the new address for dst
to be set within the function. As you currently have your code, you would have to change the function return type to char *
and assign the return to a character pointer variable in main()
, otherwise your changes to dst
in strlcat
would never be reflected back in main()
)
Example Use/Output
$ ./bin/strlcat
src: hello
dst: world
str: hello world
len: 11 chars.
$ ./bin/strlcat C-Programming can-do
src: C-Programming
dst: can-do
str: C-Programming can-do
len: 20 chars.
Always run any program you dynamically allocate memory in through a memory error checking program. For Linux, valgrind
is the normal choice, but there are similar tools for all platforms,
Memory Use/Error Check
$ valgrind ./bin/strlcat C-Programming can-do
==15976== Memcheck, a memory error detector
==15976== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==15976== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==15976== Command: ./bin/strlcat C-Programming can-do
==15976==
src: C-Programming
dst: can-do
str: C-Programming can-do
len: 20 chars.
==15976==
==15976== HEAP SUMMARY:
==15976== in use at exit: 0 bytes in 0 blocks
==15976== total heap usage: 2 allocs, 2 frees, 36 bytes allocated
==15976==
==15976== All heap blocks were freed -- no leaks are possible
==15976==
==15976== For counts of detected and suppressed errors, rerun with: -v
==15976== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always make sure there are no errors and that you have freed all memory you have allocated.
Look things over and let me know if I understood where you problem area was and whether or not you have further questions.