/*
 * strl - size-bounded string copying and concatenation
 *
 * Copyright (C) 2021,2026  Landon Curt Noll
 *
 * Calc is open software; you can redistribute it and/or modify it under
 * the terms of the version 2.1 of the GNU Lesser General Public License
 * as published by the Free Software Foundation.
 *
 * Calc is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
 * Public License for more details.
 *
 * A copy of version 2.1 of the GNU Lesser General Public License is
 * distributed with calc under the filename COPYING-LGPL.  You should have
 * received a copy with calc; if not, write to Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Under source code control:   2021/03/08 21;58:10
 * File existed as early as:    2021
 *
 * Share and enjoy!  :-)        http://www.isthe.com/chongo/tech/comp/calc/
 */

/*
 * important <system> header includes
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * calc local src includes
 */
#include "strl.h"

#include "banned.h" /* include after all other includes */

#if !defined(HAVE_STRLCPY) || defined(STRL_TEST)
/*
 * calc_strlcpy - calc STRL_TEST test version of strlcpy
 * strlcpy - size-bounded string copying
 *
 * The strlcpy() functions copies up to dstsize-1 characters from the
 * string src to dst, NUL-terminating the result if dstsize > 0.
 *
 * This function attempts to simulate strlcpy() on systems that
 * do not have this function in their C library.
 *
 * given:
 *      dst             destination string
 *      src             source string
 *      dstsize         size of the destination string buffer including NUL
 *
 * returns:
 *      total           length of source string excluding NUL,
 *                      0 ==> invalid arguments
 *
 * NOTE: If return value is >= dstsize, the result was truncated.
 *       I.e., there was not enough room in dst and so the copy
 *       was stopped before it would have overflowed and was NUL terminated.
 */
#  if defined(STRL_TEST)

size_t
calc_strlcpy(char *dst, const char *src, size_t dstsize)

#  else
size_t
strlcpy(char *dst, const char *src, size_t dstsize)

#  endif
{
    size_t srclen; /* src string length not including NUL */

    /*
     * firewall
     */
    if (dst == NULL || src == NULL || dstsize <= 0) {
        /* nothing can be copied */
#  if defined(STRL_TEST)

        printf("in %s: return 0\n", __FUNCTION__);

#  endif
        return 0;
    }

    /*
     * determine how much string we could copy
     */
    srclen = strlen(src);

    /*
     * perform the size limited copy and NUL terminate
     */
    if (srclen + 1 > dstsize) {
        memcpy(dst, src, dstsize - 1);
        dst[dstsize - 1] = '\0';
#  if defined(STRL_TEST)

        printf("in %s: if memcpy(\"%s\", \"%s\", %zu)\n", __FUNCTION__, dst, src, dstsize - 1);

#  endif
    } else {
        memcpy(dst, src, srclen);
        dst[srclen] = '\0';
#  if defined(STRL_TEST)

        printf("in %s: else memcpy(\"%s\", \"%s\", %zu)\n", __FUNCTION__, dst, src, srclen);

#  endif
    }

    /*
     * return the length we tried to copy, not including NUL
     */
#  if defined(STRL_TEST)

    printf("in %s: return %zu\n", __FUNCTION__, srclen);

#  endif
    return srclen;
}
#endif

#if !defined(HAVE_STRLCAT) || defined(STRL_TEST)
/*
 * calc_strlcat - calc STRL_TEST test version of strlcat
 * strlcat - size-bounded string cat
 *
 * The strlcat() appends string src to the end of dst.
 * At most, dstsize - strlen(dst) - 1 characters will of appended.
 *
 * This function attempts to simulate strlcat() on systems that
 * do not have this function in their C library.
 *
 * given:
 *      dst             destination string
 *      src             source string
 *      dstsize         size of the destination string buffer including NUL
 *
 * returns:
 *      total           length of string could have been copied excluding NUL,
 *                      0 ==> invalid arguments
 *
 * NOTE: If return value is >= dstsize, the output string was truncated.
 *       I.e., there was not enough room and so the result was truncated
 *       and NUL terminated.
 */
#  if defined(STRL_TEST)

size_t
calc_strlcat(char *dst, const char *src, size_t dstsize)

#  else

size_t
strlcat(char *dst, const char *src, size_t dstsize)

#  endif
{
    size_t srclen; /* src string length not including NUL */
    size_t dstlen; /* dst string length not including NUL */
    size_t catlen; /* amount from src we can copy */

    /*
     * firewall
     */
    if (dst == NULL || src == NULL || dstsize <= 0) {
        /* nothing can be concatenated */
#  if defined(STRL_TEST)

        printf("in %s: return 0\n", __FUNCTION__);

#  endif
        return 0;
    }

    /*
     * prep to determine the room we have
     */
    dstlen = strlen(dst);
    srclen = strlen(src);
#  if defined(STRL_TEST)

    printf("in %s: dst = ((%s)) src = ((%s)) dstsize = %zu\n", __FUNCTION__, dst, src, dstsize);
    printf("in %s: dstlen = %zu srclen = %zu\n", __FUNCTION__, dstlen, srclen);

#  endif
    if (dstsize <= dstlen + 1) {
        /* dst is already full */
#  if defined(STRL_TEST)

        printf("in %s: dstsize: %zu <= dstlen+1: %zu\n", __FUNCTION__, dstsize, dstlen + 1);
        printf("in %s: already full return %zu\n", __FUNCTION__, srclen + dstlen);

#  endif
        return srclen + dstlen;
    }

    /*
     * determine the amount data we can copy
     */
    catlen = dstsize - dstlen;

    /*
     * perform the concatenation
     */
    dst += dstlen;
#  if defined(STRL_TEST)

    printf("in %s: catlen = %zu\n", __FUNCTION__, catlen);

#  endif
    if (catlen > srclen + 1) {
        memcpy(dst, src, srclen);
        dst[srclen] = '\0';
#  if defined(STRL_TEST)

        printf("in %s: if memcpy(\"%s\", \"%s\", %zu)\n", __FUNCTION__, dst, src, srclen);

#  endif
    } else {
        memcpy(dst, src, catlen);
        dst[catlen] = '\0';
#  if defined(STRL_TEST)

        printf("in %s: if memcpy(\"%s\", \"%s\", %zu)\n", __FUNCTION__, dst, src, catlen);

#  endif
    }

    /*
     * return the length, not including NUL, of what could have been formed
     */
#  if defined(STRL_TEST)

    printf("in %s: return %zu\n", __FUNCTION__, srclen + dstlen);

#  endif
    return srclen + dstlen;
}
#endif

#if defined(STRL_TEST)

static char src[] = "abcde"; /* test source string */

/*
 * Main routine to test the strlcpy() and strlcat() functions.
 */
int
main(int argc, char **argv)
{
    char dst[sizeof(src)]; /* test destination string */
    size_t ret;            /* strlcpy() or strlcat() return */

    /*
     * normal copy
     */
    memset(dst, 0, sizeof(dst));
    printf("src: %s\n", src);
    ret = calc_strlcpy(dst, src, sizeof(dst));
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * truncated copy
     */
    memset(dst, 0, sizeof(dst));
    printf("src: %s\n", src);
    ret = calc_strlcpy(dst, src, sizeof(dst) - 3);
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * overcat
     */
    printf("src: %s\n", src);
    ret = calc_strlcat(dst, src, sizeof(dst));
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * fullcat
     */
    printf("src: %s\n", src);
    ret = calc_strlcat(dst, src, sizeof(dst));
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * nilcat
     */
    dst[0] = '\0';
    printf("src: %s\n", src);
    ret = calc_strlcat(dst, src, sizeof(dst));
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * shortcat
     */
    dst[1] = '\0';
    printf("src: %s\n", src);
    ret = calc_strlcat(dst, src, sizeof(dst) - 3);
    printf("ret: %zu\n", ret);
    printf("dst: %s\n\n", dst);

    /*
     * all done
     */
    exit(0);
}

#endif
