/*
	compat: Some compatibility functions (basic memory and string stuff)

	The mpg123 code is determined to keep it's legacy. A legacy of old, old UNIX.
	So anything possibly somewhat advanced should be considered to be put here, with proper #ifdef;-)

	copyright 2007-2023 by the mpg123 project
	free software under the terms of the LGPL 2.1
	see COPYING and AUTHORS files in distribution or http://mpg123.org
	initially written by Thomas Orgis, Windows Unicode stuff by JonY.
*/
// Need POSIX 2008 for uselocale stuff.
#define _POSIX_C_SOURCE 200809L
#include "compat.h"

/* Win32 is only supported with unicode now. These headers also cover
   module stuff. The WANT_WIN32_UNICODE macro is synonymous with
   "want windows-specific API, and only the unicode variants of which". */
#if defined (_WIN32) || defined (__CYGWIN__)
#include <wchar.h>
#include <windows.h>
#include <winnls.h>
#endif

#if HAVE_LOCALE_H
#include <locale.h>
#endif

#include "../common/debug.h"

/* A safe realloc also for very old systems where realloc(NULL, size) returns NULL. */
void *INT123_safe_realloc(void *ptr, size_t size)
{
	if(ptr == NULL) return malloc(size);
	else return realloc(ptr, size);
}

// A more sensible variant of realloc: It deallocates the original memory if
// realloc fails or if size zero was requested.
void *INT123_safer_realloc(void *ptr, size_t size)
{
	void *nptr = size ? INT123_safe_realloc(ptr, size) : NULL;
	if(!nptr && ptr)
		free(ptr);
	return nptr;
}

const char *INT123_strerror(int errnum)
{
#if defined(HAVE_STRERROR_L) && defined(HAVE_USELOCALE)
	locale_t curloc = uselocale((locale_t)0);
	if(curloc != LC_GLOBAL_LOCALE)
		return strerror_l(errnum, curloc);
#endif
// Also fall back to strerror() in case of no set locale.
#if defined(HAVE_STRERROR)
	return strerror(errnum);
#else
	extern int sys_nerr;
	extern char *sys_errlist[];

	return (errnum < sys_nerr) ?  sys_errlist[errnum]  :  "";
#endif
}

char* INT123_compat_strdup(const char *src)
{
	char *dest = NULL;
	if(src)
	{
		size_t len;
		len = strlen(src)+1;
		if((dest = malloc(len)))
			memcpy(dest, src, len);
	}
	return dest;
}

/* Windows Unicode stuff */
/* Provided unconditionally, since WASAPI needs it to configure the audio device */
#if defined (_WIN32) || defined (__CYGWIN__)
static
int win32_wide_common(const wchar_t * const wptr, char **mbptr, size_t * buflen, UINT cp)
{
  size_t len;
  char *buf;
  int ret = 0;

  len = WideCharToMultiByte(cp, 0, wptr, -1, NULL, 0, NULL, NULL); /* Get utf-8 string length */
  buf = calloc(len + 1, sizeof (char)); /* Can we assume sizeof char always = 1? */

  if(!buf) len = 0;
  else {
    if (len != 0) ret = WideCharToMultiByte(cp, 0, wptr, -1, buf, len, NULL, NULL); /*Do actual conversion*/
    buf[len] = '0'; /* Must terminate */
  }
  *mbptr = buf; /* Set string pointer to allocated buffer */
  if(buflen != NULL) *buflen = (len) * sizeof (char); /* Give length of allocated memory if needed. */
  return ret;
}

int INT123_win32_wide_utf8(const wchar_t * const wptr, char **mbptr, size_t * buflen)
{
  return win32_wide_common(wptr, mbptr, buflen, CP_UTF8);
}

int INT123_win32_wide_utf7(const wchar_t * const wptr, char **mbptr, size_t * buflen)
{
  return win32_wide_common(wptr, mbptr, buflen, CP_UTF7);
}

int INT123_win32_utf8_wide(const char *const mbptr, wchar_t **wptr, size_t *buflen)
{
  size_t len;
  wchar_t *buf;
  int ret = 0;

  len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, NULL, 0); /* Get converted size */
  buf = calloc(len + 1, sizeof (wchar_t)); /* Allocate memory accordingly */

  if(!buf) len = 0;
  else {
    if (len != 0) ret = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, mbptr, -1, buf, len); /* Do conversion */
    buf[len] = L'0'; /* Must terminate */
  }
  *wptr = buf; /* Set string pointer to allocated buffer */
  if (buflen != NULL) *buflen = len * sizeof (wchar_t); /* Give length of allocated memory if needed. */
  return ret; /* Number of characters written */
}
#endif
