/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 * VgaGames 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include "misc.h"

void init_misc_str(void);

static size_t misc_strcpy(char *, size_t, const char *);
static size_t misc_strscpy(char *, size_t, const char *, size_t);
static size_t misc_strccat(char *, size_t, ...);


/* set functions */
void
init_misc_str(void)
{
  vg4->misc->strcpy = misc_strcpy;
  vg4->misc->strscpy = misc_strscpy;
  vg4->misc->strccat = misc_strccat;
} /* Ende init_misc_str */


/* misc_strcpy:
 * copy src into dest with maximal size of dsize (including terminating null),
 * dest will always be null-terminated and remaining space will be filled with 0
 * @param dest   destination
 * @param dsize  maximal bytes to copy including terminating null
 * @param src    source
 * @return  number of copied bytes (excluding terminating null)
 *
 * This function does not copy more than dsize bytes (including terminating null).
 * If the copying was truncated due to this limit,
 * then the return value is the number of bytes (excluding terminating null),
 * which would have been copied if enough space had been available.
 * Thus, a return value of dsize or more means that the copying was truncated.
 */
static size_t
misc_strcpy(char *dest, size_t dsize, const char *src)
{
  size_t ipos;

  if (dest == NULL || dsize == 0) { return 0; }
  memset(dest, 0, dsize);
  if (src == NULL) { return 0; }

  for (ipos = 0; ipos < dsize; ipos++) {
    dest[ipos] = src[ipos];
    if (src[ipos] == '\0') { break; }
  }

  if (ipos == dsize) {
    dest[dsize - 1] = '\0';
    ipos += strlen(src + ipos);
  }

  return ipos;
} /* Ende misc_strcpy */


/* misc_strscpy:
 * copy src with maximal size of ssize (if no terminating null before)
 * into dest with maximal size of dsize (including terminating null),
 * dest will always be null-terminated and remaining space will be filled with 0
 * @param dest   destination
 * @param dsize  maximal bytes to copy into dest including terminating null
 * @param src    source
 * @param ssize  maximal bytes to copy from src, if no terminating null before
 * @return  number of copied bytes (excluding terminating null)
 *
 * This function does not copy more than dsize bytes (including terminating null).
 * If the copying was truncated due to this limit,
 * then the return value is the number of bytes (excluding terminating null),
 * which would have been copied if enough space had been available.
 * Thus, a return value of dsize or more means that the copying was truncated.
 */
static size_t
misc_strscpy(char *dest, size_t dsize, const char *src, size_t ssize)
{
  size_t ipos;

  if (dest == NULL || dsize == 0) { return 0; }
  memset(dest, 0, dsize);
  if (src == NULL || ssize == 0) { return 0; }

  for (ipos = 0; ipos < dsize; ipos++) {
    if (ipos == ssize) { break; }
    dest[ipos] = src[ipos];
    if (src[ipos] == '\0') { break; }
  }

  if (ipos == dsize) {
    dest[dsize - 1] = '\0';
    for (; ipos < ssize; ipos++) {
      if (src[ipos] == '\0') { break; }
    }
  }

  return ipos;
} /* Ende misc_strscpy */


/* misc_strccat:
 * concatenate all strings into dest with maximal size of dsize (including terminating null),
 * dest will always be null-terminated and remaining space will be filled with 0
 * @param dest   destination
 * @param dsize  maximal bytes to copy including terminating null
 * @param ...    further parameters are strings,
 *               last parameter must be NULL
 * @return  number of copied bytes (excluding terminating null)
 *
 * This function does not copy more than dsize bytes (including terminating null).
 * If the copying was truncated due to this limit,
 * then the return value is the number of bytes (excluding terminating null),
 * which would have been copied if enough space had been available.
 * Thus, a return value of dsize or more means that the copying was truncated.
 */
static size_t
misc_strccat(char *dest, size_t dsize, ...)
{
  size_t idst, isrc;
  va_list ap;
  const char *psrc;

  if (dest == NULL || dsize == 0) { return 0; }

  va_start(ap, dsize);
  psrc = NULL;
  isrc = 0;

  memset(dest, 0, dsize);
  for (idst = 0; idst < dsize; idst++, isrc++) {
    if (psrc == NULL) {
      psrc = (const char *)va_arg(ap, const char *);
      if (psrc == NULL) { break; }
      isrc = 0;
    }
    dest[idst] = psrc[isrc];
    if (psrc[isrc] == '\0') { idst--; psrc = NULL; }
  }

  if (idst == dsize) {
    dest[dsize - 1] = '\0';
    if (psrc != NULL) { idst += strlen(psrc + isrc); }
    for (;;) {
      psrc = (const char *)va_arg(ap, const char *);
      if (psrc == NULL) { break; }
      idst += strlen(psrc);
    }
  }

  va_end(ap);

  return idst;
} /* Ende misc_strccat */
