/////////////////////////////////////////////////////////////////////////////
// Name:        src/common/wxcrt.cpp
// Purpose:     wxChar CRT wrappers implementation
// Author:      Ove Kaven
// Modified by: Ron Lee, Francesco Montorsi
// Created:     09/04/99
// Copyright:   (c) wxWidgets copyright
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// ===========================================================================
// headers, declarations, constants
// ===========================================================================

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"


#include "wx/crt.h"
#include "wx/strconv.h" // wxMBConv::cWC2MB()

#define _ISOC9X_SOURCE 1 // to get vsscanf()
#define _BSD_SOURCE    1 // to still get strdup()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

#ifdef __SGI__
    // wide character functions are declared in std namespace under IRIX
    using namespace std;

    // and this one is only declared if __c99 is defined which is not the case
    // for C++ builds, so declare it ourselves
    extern "C" int vswscanf(const wchar_t *, const wchar_t *, va_list);
#endif

#include <time.h>
#include <locale.h>

#ifndef WX_PRECOMP
    #include "wx/string.h"
    #include "wx/utils.h"     // for wxMin and wxMax
    #include "wx/log.h"
#endif

#ifdef HAVE_LANGINFO_H
    #include <langinfo.h>
#endif

#include <errno.h>

wxDECL_FOR_STRICT_MINGW32(int, vswprintf, (wchar_t*, const wchar_t*, __VALIST))
wxDECL_FOR_STRICT_MINGW32(int, _putws, (const wchar_t*))
wxDECL_FOR_STRICT_MINGW32(void, _wperror, (const wchar_t*))

WXDLLIMPEXP_BASE size_t wxMB2WC(wchar_t *buf, const char *psz, size_t n)
{
  // assume that we have mbsrtowcs() too if we have wcsrtombs()
#ifdef HAVE_WCSRTOMBS
  mbstate_t mbstate;
  memset(&mbstate, 0, sizeof(mbstate_t));
#endif

  if (buf) {
    if (!n || !*psz) {
      if (n) *buf = wxT('\0');
      return 0;
    }
#ifdef HAVE_WCSRTOMBS
    return mbsrtowcs(buf, &psz, n, &mbstate);
#else
    return wxMbstowcs(buf, psz, n);
#endif
  }

  // Note that we rely on common (and required by Unix98 but unfortunately not
  // C99) extension which allows to call mbs(r)towcs() with null output pointer
  // to just get the size of the needed buffer -- this is needed as otherwise
  // we have no idea about how much space we need. Currently all supported
  // compilers do provide it and if they don't, HAVE_WCSRTOMBS shouldn't be
  // defined at all.
#ifdef HAVE_WCSRTOMBS
  return mbsrtowcs(nullptr, &psz, 0, &mbstate);
#else
  return wxMbstowcs(nullptr, psz, 0);
#endif
}

WXDLLIMPEXP_BASE size_t wxWC2MB(char *buf, const wchar_t *pwz, size_t n)
{
#ifdef HAVE_WCSRTOMBS
  mbstate_t mbstate;
  memset(&mbstate, 0, sizeof(mbstate_t));
#endif

  if (buf) {
    if (!n || !*pwz) {
      // glibc2.1 chokes on null input
      if (n) *buf = '\0';
      return 0;
    }
#ifdef HAVE_WCSRTOMBS
    return wcsrtombs(buf, &pwz, n, &mbstate);
#else
    return wxWcstombs(buf, pwz, n);
#endif
  }

#ifdef HAVE_WCSRTOMBS
  return wcsrtombs(nullptr, &pwz, 0, &mbstate);
#else
  return wxWcstombs(nullptr, pwz, 0);
#endif
}

char* wxSetlocale(int category, const char *locale)
{
    char *rv = setlocale(category, locale);
    if ( locale != nullptr /* setting locale, not querying */ &&
         rv /* call was successful */ )
    {
        wxUpdateLocaleIsUtf8();
    }
    return rv;
}

// ============================================================================
// printf() functions business
// ============================================================================

// special test mode: define all functions below even if we don't really need
// them to be able to test them
#ifdef wxTEST_PRINTF
    #undef wxFprintf
    #undef wxPrintf
    #undef wxSprintf
    #undef wxVfprintf
    #undef wxVsprintf
    #undef wxVprintf
    #undef wxVsnprintf_

    #define wxNEED_WPRINTF

    int wxCRT_VfprintfW( FILE *stream, const wchar_t *format, va_list argptr );
#endif

// ----------------------------------------------------------------------------
// implement the standard IO functions for wide char if libc doesn't have them
// ----------------------------------------------------------------------------

#ifndef wxCRT_FputsW
int wxCRT_FputsW(const wchar_t *ws, FILE *stream)
{
    wxCharBuffer buf(wxConvLibc.cWC2MB(ws));
    if ( !buf )
        return -1;

    // counting the number of wide characters written isn't worth the trouble,
    // simply distinguish between ok and error
    return wxCRT_FputsA(buf, stream) == -1 ? -1 : 0;
}
#endif // !wxCRT_FputsW

#ifndef wxCRT_PutsW
int wxCRT_PutsW(const wchar_t *ws)
{
    int rc = wxCRT_FputsW(ws, stdout);
    if ( rc != -1 )
    {
        if ( wxCRT_FputsW(L"\n", stdout) == -1 )
            return -1;

        rc++;
    }

    return rc;
}
#endif // !wxCRT_PutsW

#ifndef wxCRT_FputcW
int /* not wint_t */ wxCRT_FputcW(wchar_t wc, FILE *stream)
{
    wchar_t ws[2] = { wc, L'\0' };

    return wxCRT_FputsW(ws, stream);
}
#endif // !wxCRT_FputcW

// NB: we only implement va_list functions here, the ones taking ... are
//     defined below for wxNEED_PRINTF_CONVERSION case anyhow and we reuse
//     the definitions there to avoid duplicating them here
#ifdef wxNEED_WPRINTF

// TODO: implement the scanf() functions
static int vwscanf(const wchar_t *format, va_list argptr)
{
    wxFAIL_MSG( wxT("TODO") );

    return -1;
}

static int vfwscanf(FILE *stream, const wchar_t *format, va_list argptr)
{
    wxFAIL_MSG( wxT("TODO") );

    return -1;
}

#define vswprintf wxCRT_VsnprintfW

static int vfwprintf(FILE *stream, const wchar_t *format, va_list argptr)
{
    wxString s;
    int rc = s.PrintfV(format, argptr);

    if ( rc != -1 )
    {
        // we can't do much better without Unicode support in libc...
        if ( fprintf(stream, "%s", (const char*)s.mb_str() ) == -1 )
            return -1;
    }

    return rc;
}

static int vwprintf(const wchar_t *format, va_list argptr)
{
    return wxCRT_VfprintfW(stdout, format, argptr);
}

#endif // wxNEED_WPRINTF

#ifdef wxNEED_VSWSCANF
static int vswscanf(const wchar_t *ws, const wchar_t *format, va_list argptr)
{
    // The best we can do without proper Unicode support in glibc is to
    // convert the strings into MB representation and run ANSI version
    // of the function. This doesn't work with %c and %s because of difference
    // in size of char and wchar_t, though.

    wxCHECK_MSG( wxStrstr(format, L"%s") == nullptr, -1,
                 wxT("incomplete vswscanf implementation doesn't allow %s") );
    wxCHECK_MSG( wxStrstr(format, L"%c") == nullptr, -1,
                 wxT("incomplete vswscanf implementation doesn't allow %c") );

    return wxCRT_VsscanfA(static_cast<const char*>(wxConvLibc.cWC2MB(ws)),
        wxConvLibc.cWC2MB(format), argptr);
}
#endif

// ----------------------------------------------------------------------------
// wxPrintf(), wxScanf() and relatives
// ----------------------------------------------------------------------------

// FIXME-UTF8: do format conversion using (modified) wxFormatConverter in
//             template wrappers, not here; note that it will needed to
//             translate all forms of string specifiers to %(l)s for wxPrintf(),
//             but it only should do what it did in 2.8 for wxScanf()!

#ifndef wxCRT_PrintfW
int wxCRT_PrintfW( const wchar_t *format, ... )
{
    va_list argptr;
    va_start(argptr, format);

    int ret = vwprintf( format, argptr );

    va_end(argptr);

    return ret;
}
#endif

#ifndef wxCRT_FprintfW
int wxCRT_FprintfW( FILE *stream, const wchar_t *format, ... )
{
    va_list argptr;
    va_start( argptr, format );

    int ret = vfwprintf( stream, format, argptr );

    va_end(argptr);

    return ret;
}
#endif

#ifndef wxCRT_VfprintfW
int wxCRT_VfprintfW( FILE *stream, const wchar_t *format, va_list argptr )
{
    return vfwprintf( stream, format, argptr );
}
#endif

#ifndef wxCRT_VprintfW
int wxCRT_VprintfW( const wchar_t *format, va_list argptr )
{
    return vwprintf( format, argptr );
}
#endif

#ifndef wxCRT_VsprintfW
int wxCRT_VsprintfW( wchar_t *str, const wchar_t *format, va_list argptr )
{
    // same as for wxSprintf()
    return vswprintf(str, INT_MAX / 4, format, argptr);
}
#endif

#ifndef wxCRT_ScanfW
int wxCRT_ScanfW(const wchar_t *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
   int ret = std::vwscanf(format, argptr);
#else
   int ret = vwscanf(format, argptr);
#endif
#else
   int ret = vwscanf(format, argptr);
#endif

    va_end(argptr);

    return ret;
}
#endif

#ifndef wxCRT_SscanfW
int wxCRT_SscanfW(const wchar_t *str, const wchar_t *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
   int ret = std::vswscanf(str, format, argptr);
#else
   int ret = vswscanf(str, format, argptr);
#endif
#else
   int ret = vswscanf(str, format, argptr);
#endif

    va_end(argptr);

    return ret;
}
#endif

#ifndef wxCRT_FscanfW
int wxCRT_FscanfW(FILE *stream, const wchar_t *format, ...)
{
    va_list argptr;
    va_start(argptr, format);
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
   int ret = std::vfwscanf(stream, format, argptr);
#else
   int ret = vfwscanf(stream, format, argptr);
#endif
#else
   int ret = vfwscanf(stream, format, argptr);
#endif

    va_end(argptr);

    return ret;
}
#endif

#ifndef wxCRT_VsscanfW
int wxCRT_VsscanfW(const wchar_t *str, const wchar_t *format, va_list argptr)
{
#ifdef __VMS
#if (__DECCXX_VER >= 70100000) && !defined(__STD_CFRONT) && !defined( __NONAMESPACE_STD )
   return std::vswscanf(str, format, argptr);
#else
   return vswscanf(str, format, argptr);
#endif
#else
   return vswscanf(str, format, argptr);
#endif
}
#endif


// ----------------------------------------------------------------------------
// wrappers to printf and scanf function families
// ----------------------------------------------------------------------------

#if !wxUSE_UTF8_LOCALE_ONLY
int wxDoSprintfWchar(char *str, const wxChar *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsprintf(str, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // !wxUSE_UTF8_LOCALE_ONLY

#if wxUSE_UNICODE_UTF8
int wxDoSprintfUtf8(char *str, const char *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsprintf(str, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // wxUSE_UNICODE_UTF8

#if !wxUSE_UTF8_LOCALE_ONLY
int wxDoSprintfWchar(wchar_t *str, const wxChar *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsprintf(str, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // !wxUSE_UTF8_LOCALE_ONLY

#if wxUSE_UNICODE_UTF8
int wxDoSprintfUtf8(wchar_t *str, const char *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsprintf(str, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // wxUSE_UNICODE_UTF8

#if !wxUSE_UTF8_LOCALE_ONLY
int wxDoSnprintfWchar(char *str, size_t size, const wxChar *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsnprintf(str, size, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // !wxUSE_UTF8_LOCALE_ONLY

#if wxUSE_UNICODE_UTF8
int wxDoSnprintfUtf8(char *str, size_t size, const char *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsnprintf(str, size, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // wxUSE_UNICODE_UTF8

#if !wxUSE_UTF8_LOCALE_ONLY
int wxDoSnprintfWchar(wchar_t *str, size_t size, const wxChar *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsnprintf(str, size, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // !wxUSE_UTF8_LOCALE_ONLY

#if wxUSE_UNICODE_UTF8
int wxDoSnprintfUtf8(wchar_t *str, size_t size, const char *format, ...)
{
    va_list argptr;
    va_start(argptr, format);

    int rv = wxVsnprintf(str, size, format, argptr);

    va_end(argptr);
    return rv;
}
#endif // wxUSE_UNICODE_UTF8


#ifdef HAVE_BROKEN_VSNPRINTF_DECL
    #define vsnprintf wx_fixed_vsnprintf
#endif

namespace
{

#if !wxUSE_UTF8_LOCALE_ONLY
int ConvertStringToBuf(const wxString& s, char *out, size_t outsize)
{
    const wxCharBuffer buf(s.mb_str());

    const size_t len = buf.length();
    if ( outsize > len )
    {
        memcpy(out, buf, len+1);
    }
    else // not enough space
    {
        memcpy(out, buf, outsize-1);
        out[outsize-1] = '\0';
    }

    return len;
}
#endif // !wxUSE_UTF8_LOCALE_ONLY

#if wxUSE_UNICODE_UTF8
int ConvertStringToBuf(const wxString& s, wchar_t *out, size_t outsize)
{
    const wxWX2WCbuf buf(s.wc_str());
    size_t len = s.length(); // same as buf length for wchar_t*
    if ( outsize > len )
    {
        memcpy(out, buf, (len+1) * sizeof(wchar_t));
    }
    else // not enough space
    {
        memcpy(out, buf, (outsize-1) * sizeof(wchar_t));
        out[outsize-1] = 0;
    }
    return len;
}
#endif // wxUSE_UNICODE_UTF8

} // anonymous namespace

template<typename T>
static size_t PrintfViaString(T *out, size_t outsize,
                              const wxString& format, va_list argptr)
{
    wxString s;
    s.PrintfV(format, argptr);

    return ConvertStringToBuf(s, out, outsize);
}

int wxVsprintf(char *str, const wxString& format, va_list argptr)
{
#if wxUSE_UTF8_LOCALE_ONLY
    return wxCRT_VsprintfA(str, format.wx_str(), argptr);
#else
    #if wxUSE_UNICODE_UTF8
    if ( wxLocaleIsUtf8 )
        return wxCRT_VsprintfA(str, format.wx_str(), argptr);
    else
    #endif
    return PrintfViaString(str, wxNO_LEN, format, argptr);
#endif
}

int wxVsprintf(wchar_t *str, const wxString& format, va_list argptr)
{
#if wxUSE_UNICODE_WCHAR
    return wxCRT_VsprintfW(str, format.wc_str(), argptr);
#else // wxUSE_UNICODE_UTF8
    #if !wxUSE_UTF8_LOCALE_ONLY
    if ( !wxLocaleIsUtf8 )
        return wxCRT_VsprintfW(str, format.wc_str(), argptr);
    else
    #endif
        return PrintfViaString(str, wxNO_LEN, format, argptr);
#endif // wxUSE_UNICODE_UTF8
}

int wxVsnprintf(char *str, size_t size, const wxString& format, va_list argptr)
{
    int rv;
#if wxUSE_UTF8_LOCALE_ONLY
    rv = wxCRT_VsnprintfA(str, size, format.wx_str(), argptr);
#else
    #if wxUSE_UNICODE_UTF8
    if ( wxLocaleIsUtf8 )
        rv = wxCRT_VsnprintfA(str, size, format.wx_str(), argptr);
    else
    #endif
    {
        // NB: if this code is called, then wxString::PrintV() would use the
        //     wchar_t* version of wxVsnprintf(), so it's safe to use PrintV()
        //     from here
        rv = PrintfViaString(str, size, format, argptr);
    }
#endif

    // VsnprintfTestCase reveals that glibc's implementation of vswprintf
    // doesn't nul terminate on truncation.
    str[size - 1] = 0;

    return rv;
}

int wxVsnprintf(wchar_t *str, size_t size, const wxString& format, va_list argptr)
{
    int rv;

#if wxUSE_UNICODE_WCHAR
    rv = wxCRT_VsnprintfW(str, size, format.wc_str(), argptr);
#else // wxUSE_UNICODE_UTF8
    #if !wxUSE_UTF8_LOCALE_ONLY
    if ( !wxLocaleIsUtf8 )
        rv = wxCRT_VsnprintfW(str, size, format.wc_str(), argptr);
    else
    #endif
    {
        // NB: if this code is called, then wxString::PrintV() would use the
        //     char* version of wxVsnprintf(), so it's safe to use PrintV()
        //     from here
        rv = PrintfViaString(str, size, format, argptr);
    }
#endif // wxUSE_UNICODE_UTF8

    // VsnprintfTestCase reveals that glibc's implementation of vswprintf
    // doesn't nul terminate on truncation.
    if ( size )
        str[size - 1] = 0;

    return rv;
}

// ----------------------------------------------------------------------------
// ctype.h stuff (currently unused)
// ----------------------------------------------------------------------------

#ifndef wxCRT_StrdupA
WXDLLIMPEXP_BASE char *wxCRT_StrdupA(const char *s)
{
    return strcpy((char *)malloc(strlen(s) + 1), s);
}
#endif // wxCRT_StrdupA

#ifndef wxCRT_StrdupW
WXDLLIMPEXP_BASE wchar_t * wxCRT_StrdupW(const wchar_t *pwz)
{
  size_t size = (wxWcslen(pwz) + 1) * sizeof(wchar_t);
  wchar_t *ret = (wchar_t *) malloc(size);
  memcpy(ret, pwz, size);
  return ret;
}
#endif // wxCRT_StrdupW

#ifndef wxWCHAR_T_IS_WXCHAR16
size_t wxStrlen(const wxChar16 *s )
{
    if (!s) return 0;
    size_t i=0;
    while (*s!=0) { ++i; ++s; }
    return i;
}

wxChar16* wxStrdup(const wxChar16* s)
{
  size_t size = (wxStrlen(s) + 1) * sizeof(wxChar16);
  wxChar16 *ret = (wxChar16*) malloc(size);
  memcpy(ret, s, size);
  return ret;
}
#endif

#ifndef wxWCHAR_T_IS_WXCHAR32
size_t wxStrlen(const wxChar32 *s )
{
    if (!s) return 0;
    size_t i=0;
    while (*s!=0) { ++i; ++s; }
    return i;
}

wxChar32* wxStrdup(const wxChar32* s)
{
  size_t size = (wxStrlen(s) + 1) * sizeof(wxChar32);
  wxChar32 *ret = (wxChar32*) malloc(size);
  if ( ret )
      memcpy(ret, s, size);
  return ret;
}
#endif

#ifndef wxCRT_StricmpA
WXDLLIMPEXP_BASE int wxCRT_StricmpA(const char *psz1, const char *psz2)
{
  char c1, c2;
  do {
    c1 = wxTolower(*psz1++);
    c2 = wxTolower(*psz2++);
  } while ( c1 && (c1 == c2) );
  return c1 - c2;
}
#endif // !defined(wxCRT_StricmpA)

#ifndef wxCRT_StricmpW
WXDLLIMPEXP_BASE int wxCRT_StricmpW(const wchar_t *psz1, const wchar_t *psz2)
{
  wchar_t c1, c2;
  do {
    c1 = wxTolower(*psz1++);
    c2 = wxTolower(*psz2++);
  } while ( c1 && (c1 == c2) );
  return c1 - c2;
}
#endif // !defined(wxCRT_StricmpW)

#ifndef wxCRT_StrnicmpA
WXDLLIMPEXP_BASE int wxCRT_StrnicmpA(const char *s1, const char *s2, size_t n)
{
  // initialize the variables just to suppress stupid gcc warning
  char c1 = 0, c2 = 0;
  while (n && ((c1 = wxTolower(*s1)) == (c2 = wxTolower(*s2)) ) && c1) n--, s1++, s2++;
  if (n) {
    if (c1 < c2) return -1;
    if (c1 > c2) return 1;
  }
  return 0;
}
#endif // !defined(wxCRT_StrnicmpA)

#ifndef wxCRT_StrnicmpW
WXDLLIMPEXP_BASE int wxCRT_StrnicmpW(const wchar_t *s1, const wchar_t *s2, size_t n)
{
  // initialize the variables just to suppress stupid gcc warning
  wchar_t c1 = 0, c2 = 0;
    while (n && ((c1 = wxTolower(*s1)) == (c2 = wxTolower(*s2)) ) && c1)
    {
        n--; s1++; s2++;
    }
  if (n) {
    if (c1 < c2) return -1;
    if (c1 > c2) return 1;
  }
  return 0;
}
#endif // !defined(wxCRT_StrnicmpW)

// ----------------------------------------------------------------------------
// string.h functions
// ----------------------------------------------------------------------------

// this (and wxCRT_StrncmpW below) are extern "C" because they are needed
// by regex code, the rest isn't needed, so it's not declared as extern "C"
#ifndef wxCRT_StrlenW
extern "C" WXDLLIMPEXP_BASE size_t wxCRT_StrlenW(const wchar_t *s)
{
    size_t n = 0;
    while ( *s++ )
        n++;

    return n;
}
#endif

// ----------------------------------------------------------------------------
// stdlib.h functions
// ----------------------------------------------------------------------------

#ifndef wxCRT_GetenvW
WXDLLIMPEXP_BASE wchar_t* wxCRT_GetenvW(const wchar_t *name)
{
    // NB: buffer returned by getenv() is allowed to be overwritten next
    //     time getenv() is called, so it is OK to use static string
    //     buffer to hold the data.
    static wxWCharBuffer value;
    value = wxConvWhateverWorks.cMB2WC(getenv(wxConvWhateverWorks.cWC2MB(name)));
    return value.data();
}
#endif // !wxCRT_GetenvW

#ifndef wxCRT_StrftimeW
WXDLLIMPEXP_BASE size_t
wxCRT_StrftimeW(wchar_t *s, size_t maxsize, const wchar_t *fmt, const struct tm *tm)
{
    if ( !maxsize )
        return 0;

    wxCharBuffer buf(maxsize);

    wxCharBuffer bufFmt(wxConvLibc.cWX2MB(fmt));
    if ( !bufFmt )
        return 0;

    size_t ret = strftime(buf.data(), maxsize, bufFmt, tm);
    if  ( !ret )
        return 0;

    wxWCharBuffer wbuf = wxConvLibc.cMB2WX(buf);
    if ( !wbuf )
        return 0;

    wxCRT_StrncpyW(s, wbuf, maxsize);
    return wxCRT_StrlenW(s);
}
#endif // !wxCRT_StrftimeW

#if !defined(wxCRT_StrtoullA) || !defined(wxCRT_StrtoullW)
template<typename T>
static wxULongLong_t
wxCRT_StrtoullBase(const T* nptr, T** endptr, int base, T* sign)
{
    wxULongLong_t sum = 0;
    wxString wxstr(nptr);
    wxString::const_iterator i = wxstr.begin();
    wxString::const_iterator end = wxstr.end();

    // Skip spaces
    while ( i != end && wxIsspace(*i) ) ++i;

    // Starts with sign?
    *sign = wxT(' ');
    if ( i != end )
    {
        T c = *i;
        if ( c == wxT('+') || c == wxT('-') )
        {
            *sign = c;
            ++i;
        }
    }

    // Starts with octal or hexadecimal prefix?
    if ( i != end && *i == wxT('0') )
    {
        ++i;
        if ( i != end )
        {
            if ( (*i == wxT('x')) || (*i == wxT('X')) )
            {
                // Hexadecimal prefix: use base 16 if auto-detecting.
                if ( base == 0 )
                    base = 16;

                // If we do use base 16, just skip "x" as well.
                if ( base == 16 )
                {
                    ++i;
                }
                else // Not using base 16
                {
                    // Then it's an error.
                    if ( endptr )
                        *endptr = const_cast<T*>(nptr);
                    errno = EINVAL;
                    return sum;
                }
            }
            else if ( base == 0 )
            {
                base = 8;
            }
        }
        else
            --i;
    }

    if ( base == 0 )
        base = 10;

    for ( ; i != end; ++i )
    {
        unsigned int n;

        T c = *i;
        if ( c >= '0' )
        {
            if ( c <= '9' )
                n = c - wxT('0');
            else
                n = wxTolower(c) - wxT('a') + 10;
        }
        else
            break;

        if ( n >= (unsigned int)base )
            // Invalid character (for this base)
            break;

        wxULongLong_t prevsum = sum;
        sum = (sum * base) + n;

        if ( sum < prevsum )
        {
            errno = ERANGE;
            break;
        }
    }

    if ( endptr )
    {
        *endptr = (T*)(nptr + (i - wxstr.begin()));
    }

    return sum;
}

template<typename T>
static wxULongLong_t wxCRT_DoStrtoull(const T* nptr, T** endptr, int base)
{
    T sign;
    wxULongLong_t uval = ::wxCRT_StrtoullBase(nptr, endptr, base, &sign);

    if ( sign == wxT('-') )
    {
        errno = ERANGE;
        uval = 0;
    }

    return uval;
}
#endif // !defined(wxCRT_StrtoullA) || !defined(wxCRT_StrtoullW)

#if !defined(wxCRT_StrtollA) || !defined(wxCRT_StrtollW)
template<typename T>
static wxLongLong_t wxCRT_DoStrtoll(const T* nptr, T** endptr, int base)
{
    T sign;
    wxULongLong_t uval = ::wxCRT_StrtoullBase(nptr, endptr, base, &sign);
    wxLongLong_t val = 0;

    if ( sign == wxT('-') )
    {
        if (uval <= (wxULongLong_t)wxINT64_MAX + 1)
        {
            val = -(wxLongLong_t)uval;
        }
        else
        {
            errno = ERANGE;
        }
    }
    else if ( uval <= wxINT64_MAX )
    {
        val = uval;
    }
    else
    {
        errno = ERANGE;
    }

    return val;
}
#endif // !defined(wxCRT_StrtollA) || !defined(wxCRT_StrtollW)

#ifndef wxCRT_StrtollA
wxLongLong_t wxCRT_StrtollA(const char* nptr, char** endptr, int base)
    { return wxCRT_DoStrtoll(nptr, endptr, base); }
#endif
#ifndef wxCRT_StrtollW
wxLongLong_t wxCRT_StrtollW(const wchar_t* nptr, wchar_t** endptr, int base)
    { return wxCRT_DoStrtoll(nptr, endptr, base); }
#endif

#ifndef wxCRT_StrtoullA
wxULongLong_t wxCRT_StrtoullA(const char* nptr, char** endptr, int base)
    { return wxCRT_DoStrtoull(nptr, endptr, base); }
#endif
#ifndef wxCRT_StrtoullW
wxULongLong_t wxCRT_StrtoullW(const wchar_t* nptr, wchar_t** endptr, int base)
    { return wxCRT_DoStrtoull(nptr, endptr, base); }
#endif

// ----------------------------------------------------------------------------
// strtok() functions
// ----------------------------------------------------------------------------

template<typename T>
static T *wxCRT_DoStrtok(T *psz, const T *delim, T **save_ptr)
{
    if (!psz)
    {
        psz = *save_ptr;
        if ( !psz )
            return nullptr;
    }

    psz += wxStrspn(psz, delim);
    if (!*psz)
    {
        *save_ptr = nullptr;
        return nullptr;
    }

    T *ret = psz;
    psz = wxStrpbrk(psz, delim);
    if (!psz)
    {
        *save_ptr = nullptr;
    }
    else
    {
        *psz = wxT('\0');
        *save_ptr = psz + 1;
    }

    return ret;
}

#ifndef wxCRT_StrtokA
char *wxCRT_StrtokA(char *psz, const char *delim, char **save_ptr)
    { return wxCRT_DoStrtok(psz, delim, save_ptr); }
#endif
#ifndef wxCRT_StrtokW
wchar_t *wxCRT_StrtokW(wchar_t *psz, const wchar_t *delim, wchar_t **save_ptr)
    { return wxCRT_DoStrtok(psz, delim, save_ptr); }
#endif

// ----------------------------------------------------------------------------
// missing C RTL functions
// ----------------------------------------------------------------------------

#ifdef wxNEED_STRDUP

char *strdup(const char *s)
{
    char *dest = (char*) malloc( strlen( s ) + 1 ) ;
    if ( dest )
        strcpy( dest , s ) ;
    return dest ;
}
#endif // wxNEED_STRDUP

// ============================================================================
// wxLocaleIsUtf8
// ============================================================================

#if !wxUSE_UTF8_LOCALE_ONLY
bool wxLocaleIsUtf8 = false; // the safer setting if not known
#endif

static bool wxIsCharsetUtf8(const char* charset)
{
    if ( strcmp(charset, "UTF-8") == 0 ||
         strcmp(charset, "utf-8") == 0 ||
         strcmp(charset, "UTF8") == 0 ||
         strcmp(charset, "utf8") == 0 )
    {
        return true;
    }

    return false;
}

static bool wxIsLocaleUtf8()
{
    // NB: we intentionally don't use wxLocale::GetSystemEncodingName(),
    //     because a) it may be unavailable in some builds and b) has slightly
    //     different semantics (default locale instead of current)

    const char* charset;
#if defined(HAVE_LANGINFO_H) && defined(CODESET)
    // GNU libc provides current character set this way (this conforms to
    // Unix98)
    charset = nl_langinfo(CODESET);
    if ( charset && wxIsCharsetUtf8(charset) )
        return true;
#endif // HAVE_LANGINFO_H

    // check LC_CTYPE string: this also works with (sufficiently recent) MSVC
    // and on any other system without nl_langinfo()
    const char *lc_ctype = setlocale(LC_CTYPE, nullptr);
    if ( lc_ctype )
    {
        // check if we're running under the "C" locale: it is 7bit subset
        // of UTF-8, so it can be safely used with the UTF-8 build:
        if ( (strcmp(lc_ctype, "C") == 0 || strcmp(lc_ctype, "POSIX") == 0) )
            return true;

        // any other locale can also use UTF-8 encoding if it's explicitly
        // specified
        charset = strrchr(lc_ctype, '.');
        if ( charset && wxIsCharsetUtf8(charset + 1) )
            return true;
    }

    // by default assume that we don't use UTF-8
    return false;
}

void wxUpdateLocaleIsUtf8()
{
#if wxUSE_UTF8_LOCALE_ONLY
    if ( !wxIsLocaleUtf8() )
    {
        wxLogFatalError(wxT("This program requires UTF-8 locale to run."));
    }
#else // !wxUSE_UTF8_LOCALE_ONLY
    wxLocaleIsUtf8 = wxIsLocaleUtf8();
#endif
}

// ============================================================================
// wx wrappers for CRT functions
// ============================================================================

#if wxUSE_UNICODE_WCHAR
    #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW)  return_kw callW
#elif wxUSE_UNICODE_UTF8 && !wxUSE_UTF8_LOCALE_ONLY
    #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW) \
            return_kw wxLocaleIsUtf8 ? callA : callW
#else // ANSI or UTF8 only
    #define CALL_ANSI_OR_UNICODE(return_kw, callA, callW)  return_kw callA
#endif

int wxPuts(const wxString& s)
{
    // under IRIX putws() takes a non-const argument so use wchar_str() instead
    // of wc_str()
    CALL_ANSI_OR_UNICODE(return,
                         wxCRT_PutsA(s.mb_str()),
                         wxCRT_PutsW(s.wchar_str()));
}

int wxFputs(const wxString& s, FILE *stream)
{
    CALL_ANSI_OR_UNICODE(return,
                         wxCRT_FputsA(s.mb_str(), stream),
                         wxCRT_FputsW(s.wc_str(), stream));
}

int wxFputc(const wxUniChar& c, FILE *stream)
{
    CALL_ANSI_OR_UNICODE(return,
                         wxCRT_FputsA(c.AsUTF8(), stream),
                         wxCRT_FputcW((wchar_t)c, stream));
}

#ifdef wxCRT_PerrorA

void wxPerror(const wxString& s)
{
#ifdef wxCRT_PerrorW
    CALL_ANSI_OR_UNICODE(wxEMPTY_PARAMETER_VALUE,
                         wxCRT_PerrorA(s.mb_str()),
                         wxCRT_PerrorW(s.wc_str()));
#else
    wxCRT_PerrorA(s.mb_str());
#endif
}

#endif // wxCRT_PerrorA

wchar_t *wxFgets(wchar_t *s, int size, FILE *stream)
{
    wxCHECK_MSG( s, nullptr, "empty buffer passed to wxFgets()" );

    wxCharBuffer buf(size - 1);
    // FIXME: this reads too little data if wxConvLibc uses UTF-8 ('size' wide
    //        characters may be encoded by up to 'size'*4 bytes), but what
    //        else can we do?
    if ( wxFgets(buf.data(), size, stream) == nullptr )
        return nullptr;

    if ( wxConvLibc.ToWChar(s, size, buf, wxNO_LEN) == wxCONV_FAILED )
        return nullptr;

    return s;
}

// ----------------------------------------------------------------------------
// wxScanf() and friends
// ----------------------------------------------------------------------------

#ifdef HAVE_VSSCANF // __VISUALC__, see wx/crt.h
int wxVsscanf(const char *str, const char *format, va_list ap)
    { return wxCRT_VsscanfA(str, format, ap); }
int wxVsscanf(const wchar_t *str, const wchar_t *format, va_list ap)
    { return wxCRT_VsscanfW(str, format, ap); }
int wxVsscanf(const wxCharBuffer& str, const char *format, va_list ap)
    { return wxCRT_VsscanfA(static_cast<const char*>(str), format, ap); }
int wxVsscanf(const wxWCharBuffer& str, const wchar_t *format, va_list ap)
    { return wxCRT_VsscanfW(str, format, ap); }
int wxVsscanf(const wxString& str, const char *format, va_list ap)
    { return wxCRT_VsscanfA(static_cast<const char*>(str.mb_str()), format, ap); }
int wxVsscanf(const wxString& str, const wchar_t *format, va_list ap)
    { return wxCRT_VsscanfW(str.wc_str(), format, ap); }
int wxVsscanf(const wxCStrData& str, const char *format, va_list ap)
    { return wxCRT_VsscanfA(static_cast<const char*>(str.AsCharBuf()), format, ap); }
int wxVsscanf(const wxCStrData& str, const wchar_t *format, va_list ap)
    { return wxCRT_VsscanfW(str.AsWCharBuf(), format, ap); }
#endif // HAVE_NO_VSSCANF

// ============================================================================
// ANDROID specific private implementations (due stubs/missing support in NDK)
// ============================================================================

// On android, most wchar_t functions are broken, so instead we must
// convert a byte at a time

#ifdef __ANDROID__

#define ANDROID_WCSTO_START \
    int len = wcslen(nptr) + 1; \
    char dst[len]; \
    for(int i=0; i<len; i++) \
        dst[i] = wctob(nptr[i]); \
    char *dstendp;

#define ANDROID_WCSTO_END \
    if(endptr) { \
        if(dstendp) \
            *endptr = (wchar_t*)(nptr + (dstendp - dst)); \
        else \
            *endptr = nullptr; \
    } \
    return d;

WXDLLEXPORT long android_wcstol(const wchar_t *nptr, wchar_t **endptr, int base)
{
    ANDROID_WCSTO_START
    long d = strtol(dst, &dstendp, base);
    ANDROID_WCSTO_END
}

WXDLLEXPORT unsigned long android_wcstoul(const wchar_t *nptr, wchar_t **endptr, int base)
{
    ANDROID_WCSTO_START
    unsigned long d = strtoul(dst, &dstendp, base);
    ANDROID_WCSTO_END
}

WXDLLEXPORT double android_wcstod(const wchar_t *nptr, wchar_t **endptr)
{
    ANDROID_WCSTO_START
    double d = strtod(dst, &dstendp);
    ANDROID_WCSTO_END
}

#ifdef wxNEED_WX_MBSTOWCS

WXDLLEXPORT size_t android_mbstowcs(wchar_t * out, const char * in, size_t outlen)
{
    if (!out)
    {
        size_t outsize = 0;
        while(*in++)
            outsize++;
        return outsize;
    }

    const char* origin = in;

    while (outlen-- && *in)
    {
        *out++ = (wchar_t) *in++;
    }

    *out = '\0';

    return in - origin;
}

WXDLLEXPORT size_t android_wcstombs(char * out, const wchar_t * in, size_t outlen)
{
    if (!out)
    {
        size_t outsize = 0;
        while(*in++)
            outsize++;
        return outsize;
    }

    const wchar_t* origin = in;

    while (outlen-- && *in)
    {
        *out++ = (char) *in++;
    }

    *out = '\0';

    return in - origin;
}

#endif // wxNEED_WX_MBSTOWCS

#endif // __ANDROID__
