/* Test of POSIX compatible vasnwprintf() and asnwprintf() functions.
   Copyright (C) 2007-2025 Free Software Foundation, Inc.

   This program 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 3 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>, 2007.  */

#include <config.h>

#include "vasnwprintf.h"

#include <float.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#if __GLIBC__ >= 2 || defined __CYGWIN__
# include <langinfo.h>
# include <stdint.h>
#endif

#include "macros.h"

int
main (int argc, char *argv[])
{
  /* configure should already have checked that the locale is supported.  */
  if (setlocale (LC_ALL, "") == NULL)
    return 1;

  /* Test that a locale dependent decimal point is used.  */
  {
    size_t length;
    wchar_t *result = asnwprintf (NULL, &length, L"%.1a", 1.0);
    ASSERT (result != NULL);
    ASSERT (wcscmp (result, L"0x1,0p+0") == 0
            || wcscmp (result, L"0x2,0p-1") == 0
            || wcscmp (result, L"0x4,0p-2") == 0
            || wcscmp (result, L"0x8,0p-3") == 0);
    ASSERT (length == wcslen (result));
    free (result);
  }

  /* Test that a locale dependent decimal point is used.  */
  {
    size_t length;
    wchar_t *result = asnwprintf (NULL, &length, L"%.1La", 1.0L);
    ASSERT (result != NULL);
    ASSERT (wcscmp (result, L"0x1,0p+0") == 0
            || wcscmp (result, L"0x2,0p-1") == 0
            || wcscmp (result, L"0x4,0p-2") == 0
            || wcscmp (result, L"0x8,0p-3") == 0);
    ASSERT (length == wcslen (result));
    free (result);
  }

  /* Test that a locale dependent grouping separator is used correctly.  */
  {
    wchar_t separator[10];
#if __GLIBC__ >= 2 || defined __CYGWIN__
    /* On glibc, in the unibyte locale fr_FR, the *wprintf routines use U+202F
       as separator, which cannot be represented in the locale encoding.  */
    separator[0] = (wchar_t) (uintptr_t) nl_langinfo (_NL_NUMERIC_THOUSANDS_SEP_WC);
    separator[1] = L'\0';
#else
    const char *separator_mb =
      localeconv ()->thousands_sep; /* = nl_langinfo (THOUSEP) */
    mbstowcs (separator, separator_mb, SIZEOF (separator));
#endif
    size_t separator_len = wcslen (separator);

    /* Tests of %d directive with grouping separator.  */
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'d", 1000);
      ASSERT (result != NULL);
      ASSERT (length == 4 + separator_len);
      ASSERT (wmemcmp (result, L"1", 1) == 0
              && wmemcmp (result + 1, separator, separator_len) == 0
              && wcscmp (result + 1 + separator_len, L"000") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'d", -1000);
      ASSERT (result != NULL);
      ASSERT (length == 5 + separator_len);
      ASSERT (wmemcmp (result, L"-1", 2) == 0
              && wmemcmp (result + 2, separator, separator_len) == 0
              && wcscmp (result + 2 + separator_len, L"000") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'d", -142857);
      ASSERT (result != NULL);
      ASSERT (length == 7 + separator_len);
      ASSERT (wmemcmp (result, L"-142", 4) == 0
              && wmemcmp (result + 4, separator, separator_len) == 0
              && wcscmp (result + 4 + separator_len, L"857") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'d", 2147483647);
      ASSERT (result != NULL);
      ASSERT (length == 10 + 3 * separator_len);
      ASSERT (wmemcmp (result, L"2", 1) == 0
              && wmemcmp (result + 1, separator, separator_len) == 0
              && wmemcmp (result + 1 + separator_len, L"147", 3) == 0
              && wmemcmp (result + 4 + separator_len, separator, separator_len) == 0
              && wmemcmp (result + 4 + 2 * separator_len, L"483", 3) == 0
              && wmemcmp (result + 7 + 2 * separator_len, separator, separator_len) == 0
              && wcscmp (result + 7 + 3 * separator_len, L"647") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'d", -2147483647);
      ASSERT (result != NULL);
      ASSERT (length == 11 + 3 * separator_len);
      ASSERT (wmemcmp (result, L"-2", 2) == 0
              && wmemcmp (result + 2, separator, separator_len) == 0
              && wmemcmp (result + 2 + separator_len, L"147", 3) == 0
              && wmemcmp (result + 5 + separator_len, separator, separator_len) == 0
              && wmemcmp (result + 5 + 2 * separator_len, L"483", 3) == 0
              && wmemcmp (result + 8 + 2 * separator_len, separator, separator_len) == 0
              && wcscmp (result + 8 + 3 * separator_len, L"647") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.10d", 1000);
      ASSERT (result != NULL);
      if (length == 10 + separator_len)
        {
          /* Most implementations don't add the grouping character within the
             leading zeroes.  */
          ASSERT (length == 10 + separator_len);
          ASSERT (wmemcmp (result, L"0000001", 7) == 0
                  && wmemcmp (result + 7, separator, separator_len) == 0
                  && wcscmp (result + 7 + separator_len, L"000") == 0);
        }
      else
        {
          /* But there are good arguments for adding the grouping character
             within the leading zeroes too.
             <https://sourceware.org/bugzilla/show_bug.cgi?id=23432>  */
          ASSERT (length == 10 + 3 * separator_len);
          ASSERT (wmemcmp (result, L"0", 1) == 0
                  && wmemcmp (result + 1, separator, separator_len) == 0
                  && wmemcmp (result + 1 + separator_len, L"000", 3) == 0
                  && wmemcmp (result + 4 + separator_len, separator, separator_len) == 0
                  && wmemcmp (result + 4 + 2 * separator_len, L"001", 3) == 0
                  && wmemcmp (result + 7 + 2 * separator_len, separator, separator_len) == 0
                  && wcscmp (result + 7 + 3 * separator_len, L"000") == 0);
        }
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.10d", -1000);
      ASSERT (result != NULL);
      if (length == 11 + separator_len)
        {
          /* Most implementations don't add the grouping character within the
             leading zeroes.  */
          ASSERT (length == 11 + separator_len);
          ASSERT (wmemcmp (result, L"-0000001", 8) == 0
                  && wmemcmp (result + 8, separator, separator_len) == 0
                  && wcscmp (result + 8 + separator_len, L"000") == 0);
        }
      else
        {
          /* But there are good arguments for adding the grouping character
             within the leading zeroes too.
             <https://sourceware.org/bugzilla/show_bug.cgi?id=23432>  */
          ASSERT (length == 11 + 3 * separator_len);
          ASSERT (wmemcmp (result, L"-0", 2) == 0
                  && wmemcmp (result + 2, separator, separator_len) == 0
                  && wmemcmp (result + 2 + separator_len, L"000", 3) == 0
                  && wmemcmp (result + 5 + separator_len, separator, separator_len) == 0
                  && wmemcmp (result + 5 + 2 * separator_len, L"001", 3) == 0
                  && wmemcmp (result + 8 + 2 * separator_len, separator, separator_len) == 0
                  && wcscmp (result + 8 + 3 * separator_len, L"000") == 0);
        }
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'10d", 1000);
      ASSERT (result != NULL);
      ASSERT (length == 10);
      ASSERT (wmemcmp (result, L"      1" + separator_len, 7 - separator_len) == 0
              && wmemcmp (result + 7 - separator_len, separator, separator_len) == 0
              && wcscmp (result + 7, L"000") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'10d", -1000);
      ASSERT (result != NULL);
      ASSERT (length == 10);
      ASSERT (wmemcmp (result, L"     -1" + separator_len, 7 - separator_len) == 0
              && wmemcmp (result + 7 - separator_len, separator, separator_len) == 0
              && wcscmp (result + 7, L"000") == 0);
      free (result);
    }

    /* Tests of %f directive with grouping separator.  */
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.0f", 1000.0);
      ASSERT (result != NULL);
      ASSERT (length == 4 + separator_len);
      ASSERT (wmemcmp (result, L"1", 1) == 0
              && wmemcmp (result + 1, separator, separator_len) == 0
              && wcscmp (result + 1 + separator_len, L"000") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.5f", 3141592653.5897932386);
      ASSERT (result != NULL);
      ASSERT (length == 16 + 3 * separator_len);
      ASSERT (wmemcmp (result, L"3", 1) == 0
              && wmemcmp (result + 1, separator, separator_len) == 0
              && wmemcmp (result + 1 + separator_len, L"141", 3) == 0
              && wmemcmp (result + 4 + separator_len, separator, separator_len) == 0
              && wmemcmp (result + 4 + 2 * separator_len, L"592", 3) == 0
              && wmemcmp (result + 7 + 2 * separator_len, separator, separator_len) == 0
              && wcscmp (result + 7 + 3 * separator_len, L"653,58979") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.5f", -3141592653.5897932386);
      ASSERT (result != NULL);
      ASSERT (length == 17 + 3 * separator_len);
      ASSERT (wmemcmp (result, L"-3", 2) == 0
              && wmemcmp (result + 2, separator, separator_len) == 0
              && wmemcmp (result + 2 + separator_len, L"141", 3) == 0
              && wmemcmp (result + 5 + separator_len, separator, separator_len) == 0
              && wmemcmp (result + 5 + 2 * separator_len, L"592", 3) == 0
              && wmemcmp (result + 8 + 2 * separator_len, separator, separator_len) == 0
              && wcscmp (result + 8 + 3 * separator_len, L"653,58979") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'010.0f", 1000.0);
      ASSERT (result != NULL);
      ASSERT (length == 10);
      ASSERT (wmemcmp (result, L"0000001" + separator_len, 7 - separator_len) == 0
              && wmemcmp (result + 7 - separator_len, separator, separator_len) == 0
              && wcscmp (result + 7, L"000") == 0);
      free (result);
    }

    /* Tests of %g directive with grouping separator.  */
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'g", 1000.0);
      ASSERT (result != NULL);
      ASSERT (length == 4 + separator_len);
      ASSERT (wmemcmp (result, L"1", 1) == 0
              && wmemcmp (result + 1, separator, separator_len) == 0
              && wcscmp (result + 1 + separator_len, L"000") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'g", -142857.0);
      ASSERT (result != NULL);
      ASSERT (length == 7 + separator_len);
      ASSERT (wmemcmp (result, L"-142", 4) == 0
              && wmemcmp (result + 4, separator, separator_len) == 0
              && wcscmp (result + 4 + separator_len, L"857") == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.5g", 3141592653.5897932386);
      ASSERT (result != NULL);
      #if defined _WIN32 && !defined __CYGWIN__
        /* Native Windows uses 3 digits for the exponent.  */
        ASSERT (length == 11);
        ASSERT (wcscmp (result, L"3,1416e+009") == 0);
      #else
        ASSERT (length == 10);
        ASSERT (wcscmp (result, L"3,1416e+09") == 0);
      #endif
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.5g", -3141592653.5897932386);
      ASSERT (result != NULL);
      #if defined _WIN32 && !defined __CYGWIN__
        /* Native Windows uses 3 digits for the exponent.  */
        ASSERT (length == 12);
        ASSERT (wcscmp (result, L"-3,1416e+009") == 0);
      #else
        ASSERT (length == 11);
        ASSERT (wcscmp (result, L"-3,1416e+09") == 0);
      #endif
      free (result);
    }
    {
      size_t length;
      wchar_t *result =
        asnwprintf (NULL, &length, L"%'.50g",
                    42351647362715016953416125033982098102569580078125.0);
      ASSERT (result != NULL);
      ASSERT (length == 50 + 16 * separator_len);
      ASSERT (wmemcmp (result, L"42", 2) == 0
              && wmemcmp (result + 2, separator, separator_len) == 0
              && wmemcmp (result + 2 + separator_len, L"351", 3) == 0
              && wmemcmp (result + 5 + separator_len, separator, separator_len) == 0
              && wmemcmp (result + 5 + 2 * separator_len, L"647", 3) == 0
              && wmemcmp (result + 8 + 2 * separator_len, separator, separator_len) == 0
              && wmemcmp (result + 8 + 3 * separator_len, L"362", 3) == 0
              && wmemcmp (result + 11 + 3 * separator_len, separator, separator_len) == 0
              && wmemcmp (result + 11 + 4 * separator_len, L"715", 3) == 0
              && wmemcmp (result + 14 + 4 * separator_len, separator, separator_len) == 0);
      free (result);
    }
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'010g", 1000.0);
      ASSERT (result != NULL);
      ASSERT (length == 10);
      ASSERT (wmemcmp (result, L"0000001" + separator_len, 7 - separator_len) == 0
              && wmemcmp (result + 7 - separator_len, separator, separator_len) == 0
              && wcscmp (result + 7, L"000") == 0);
      free (result);
    }
    #if LDBL_MAX_10_EXP > 500
    {
      size_t length;
      wchar_t *result = asnwprintf (NULL, &length, L"%'.500Lg\n", 42351647362715016953416125033982098102569580078125000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0L);
      free (result);
    }
    #endif
  }

  return test_exit_status;
}
