1 /* Copyright (C) 2000-2022 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 *
_i18n_number_rewrite(CHAR_T * w,CHAR_T * rear_ptr,CHAR_T * end)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 }
112