-1

I'm trying to recreate my strlcat function from the C function library. I was looking around on the internet how it works. Basically, strlcat allows appending your source string into destination string with a NUL-terminated. The return value must be the size value after appending.

To make it short, I am pulling my strlen function into strlcat to have the size of my destination and source in the same time. I iterate through my new destination to have the length of my new string for my source string to fit. To do this, I copy the memory by using memcpy.

This is what I have (assuming we have the necessary headers):

char my_strlcat(char *dst, const char *src, size_t n)

    {
        size_t dsize;
        size_t len;
        size_t res;

        dsize = my_strlen(dst) - dst;
        len = my_strlen(src) - src;
        res = dsize + len;
        if(dsize < n)
        {
            dst+= dsize;
            n-=dsize;
            if(n <= len)
                len = n - 1;
            dst[len] = '\0';
            my_memcpy(dst,src,len);
        }
        return (res);
    }

void *my_memcpy(void *dst, const void *src, size_t num)
    {
        size_t i;

        i = -1;
        while (i++ != num)
            ((unsigned char *)dst)[i] = ((unsigned char *)src)[i];
        return (dst);
    }

size_t  my_strlen(const char *str)
{
    int len;

    len = 0;
    while (str[len] != '\0')
        len++;
    return (len);
}

    int main()
    {
        char my_strlcat_src[7] = "banana";
        char my_strlcat_dst[12] = "good";
        printf("my_strlcat => 'banana', 'good', 12 expected: 10  => %c\n", my_strlcat(my_strlcat_dst, my_strlcat_src, 12));
        char my_strlcat_src2[6] = "whatt";
        char my_strlcat_dst2[15] ="he said.";
        printf("my_strlcat => 'he said.', 'whatt', 15 expected: 13  => %c\n",my_strlcat(my_strlcat_dst2, my_strlcat_src2, 15));
    }

After I execute this program, I'm getting this error saying "invalid operands to binary expression ('size_t' (aka 'unsigned long') and 'char *')" which I quite don't really understand what is causing trouble. Is it because I am doing unsigned char in my memcpy function? What do you guys think?

Zeid Tisnes
  • 396
  • 5
  • 22

1 Answers1

0

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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I'm constraint to follow the man for strlcat. Following with `char strlcat(char *dst, const char *src, size_t n) and less than 26 code lines for the function. – Zeid Tisnes Oct 03 '17 at 08:32
  • Ahah, that's the problem. `$ man strlcat,No manual entry for strlcat`. No problem. I found a copy. You can do the same thing. I see it returns `size_t`, I'll drop a revision. – David C. Rankin Oct 03 '17 at 21:35
  • @ZeidTisnes - I located `man strlcat` and provided an additional answer reflecting its description. (and in a total of 7-lines of code) – David C. Rankin Oct 03 '17 at 22:19
  • This looks fantastic, what was your thought process? I think I was overthinking my solution in trying to implement other things and I over complicate it. I think I spent over 10 hours for this only lol. – Zeid Tisnes Oct 03 '17 at 23:20
  • Well, what I actually did, was read the `strlcat` man page and just coded it from that. What I needed to know was what the proper prototype was (I was missing it returned `size_t`). I then needed to know, how the heck it handled `dst`? Could it be `NULL`? (no, since you are passing `size` and it must be greater than zero) It can be empty, but not `NULL`. That took care of a lot of it. I then knew I was getting `dst` of `size`. Then it was just a matter of finding if `src` would fit in `size` (plus the nul-character). You can always loop `for (...; src[i]...)` so I just added the length test. – David C. Rankin Oct 03 '17 at 23:48
  • How I originally learned, was just to pull out a piece of paper and pencil and start with two short words, like `"I"` and `"can"` and write out the memory used by each, e.g. `|I|\0|` and `|c|a|n|\0|`. Then you just use the indexes to figure out how to make them `|I|c|a|n|\0|` checking against the max `size` available. Pencil and paper and memory cells and indexes really help cut through the fog. That method works for everything from pointers to linked-lists, etc.. There is no amount of staring at a screen that can replace looking at an 8.5x11 with a pencil in your hand. – David C. Rankin Oct 03 '17 at 23:58
  • That is what I try to do whenever I man. I just find it difficult to structure it from scratch through the man. Is it because I have lack of communication with others for questions? Because I feel I would learn concepts easier by asking continuous questions to my peers that helps me to picture an answer and come up with an answer together. – Zeid Tisnes Oct 04 '17 at 02:07
  • It's supposed to be hard now. C is an incredibly powerful language that gives you control at the hardware level. With great power comes great responsibility. It is up to you to account for each byte of memory use. (if it were easy, C would be full of java and C++ programmers). However, it becomes second nature with time. There is a lot to learn with C. It's not a race, so enjoy the journey. Strive to master the language, that means appreciating the details, not dreading them. Soon, you will be the one answering questions here if you keep at it.... – David C. Rankin Oct 04 '17 at 04:02
  • I have been learning to code with C++ since college started (3 years ago) and for me, it is difficult to picture out code from paper. I think I need to practice by trying to do it by paper and show it to someone to correct my thoughts properly. Right now, I am in a school where you teach yourself and I like it because of its peer to peer environment. I like your input and your help :) Your help was awesome! – Zeid Tisnes Oct 05 '17 at 05:09