1  /* Copyright (C) 2000-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <stdbool.h>
      19  #include <wchar.h>
      20  #include <wctype.h>
      21  #include <scratch_buffer.h>
      22  
      23  #include "../locale/outdigits.h"
      24  #include "../locale/outdigitswc.h"
      25  
      26  static CHAR_T *
      27  _i18n_number_rewrite (CHAR_T *w, CHAR_T *rear_ptr, CHAR_T *end)
      28  {
      29  #ifdef COMPILE_WPRINTF
      30  # define decimal NULL
      31  # define thousands NULL
      32  #else
      33    char decimal[MB_LEN_MAX + 1];
      34    char thousands[MB_LEN_MAX + 1];
      35  #endif
      36  
      37    /* "to_outpunct" is a map from ASCII decimal point and thousands-sep
      38       to their equivalent in locale. This is defined for locales which
      39       use extra decimal point and thousands-sep.  */
      40    wctrans_t map = __wctrans ("to_outpunct");
      41    wint_t wdecimal = __towctrans (L'.', map);
      42    wint_t wthousands = __towctrans (L',', map);
      43  
      44  #ifndef COMPILE_WPRINTF
      45    if (__glibc_unlikely (map != NULL))
      46      {
      47        mbstate_t state;
      48        memset (&state, '\0', sizeof (state));
      49  
      50        size_t n = __wcrtomb (decimal, wdecimal, &state);
      51        if (n == (size_t) -1)
      52  	memcpy (decimal, ".", 2);
      53        else
      54  	decimal[n] = '\0';
      55  
      56        memset (&state, '\0', sizeof (state));
      57  
      58        n = __wcrtomb (thousands, wthousands, &state);
      59        if (n == (size_t) -1)
      60  	memcpy (thousands, ",", 2);
      61        else
      62  	thousands[n] = '\0';
      63      }
      64  #endif
      65  
      66    /* Copy existing string so that nothing gets overwritten.  */
      67    CHAR_T *src;
      68    struct scratch_buffer buffer;
      69    scratch_buffer_init (&buffer);
      70    if (!scratch_buffer_set_array_size (&buffer, rear_ptr - w, sizeof (CHAR_T)))
      71      /* If we cannot allocate the memory don't rewrite the string.
      72         It is better than nothing.  */
      73      return w;
      74    src = buffer.data;
      75  
      76    CHAR_T *s = (CHAR_T *) __mempcpy (src, w,
      77  				    (rear_ptr - w) * sizeof (CHAR_T));
      78  
      79    w = end;
      80  
      81    /* Process all characters in the string.  */
      82    while (--s >= src)
      83      {
      84        if (*s >= '0' && *s <= '9')
      85  	{
      86  	  if (sizeof (CHAR_T) == 1)
      87  	    w = (CHAR_T *) outdigit_value ((char *) w, *s - '0');
      88  	  else
      89  	    *--w = (CHAR_T) outdigitwc_value (*s - '0');
      90  	}
      91        else if (__builtin_expect (map == NULL, 1) || (*s != '.' && *s != ','))
      92  	*--w = *s;
      93        else
      94  	{
      95  	  if (sizeof (CHAR_T) == 1)
      96  	    {
      97  	      const char *outpunct = *s == '.' ? decimal : thousands;
      98  	      size_t dlen = strlen (outpunct);
      99  
     100  	      w -= dlen;
     101  	      while (dlen-- > 0)
     102  		w[dlen] = outpunct[dlen];
     103  	    }
     104  	  else
     105  	    *--w = *s == '.' ? (CHAR_T) wdecimal : (CHAR_T) wthousands;
     106  	}
     107      }
     108  
     109    scratch_buffer_free (&buffer);
     110    return w;
     111  }