/*
 * strl - size-bounded string copying and concatenation
 *
 * Copyright (C) 2021  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/
 */


#include "have_string.h"
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include "strl.h"
#include "alloc.h"

#if defined(STRL_TEST)
#include <stdio.h>
#include <stdlib.h>
#endif /* STRL_TEST */


#include "banned.h"	/* include after system header <> 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 /* STRL_TEST */
size_t
strlcpy(char * dst, const char * src, size_t dstsize)
#endif /* STRL_TEST */
{
	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 /* STRL_TEST */
		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 /* STRL_TEST */
	} 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 /* STRL_TEST */
	}

	/*
	 * return the length we tried to copy, not including NUL
	 */
#if defined(STRL_TEST)
	printf("in %s: return %zu\n", __FUNCTION__, srclen);
#endif /* STRL_TEST */
	return srclen;
}
#endif /* !HAVE_STRLCPY || STRL_TEST */


#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 /* STRL_TEST */
size_t
strlcat(char * dst, const char * src, size_t dstsize)
#endif /* STRL_TEST */
{
	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 /* STRL_TEST */
		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 /* STRL_TEST */
	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 /* STRL_TEST */
		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 /* STRL_TEST */
	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 /* STRL_TEST */
	} 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 /* STRL_TEST */
	}

	/*
	 * 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 /* STRL_TEST */
	return srclen+dstlen;
}
#endif /* !HAVE_STRLCAT || STRL_TEST */

#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 /* STRL_TEST */
