2 Copyright (C) 2017-2025 Free Software Foundation, Inc.
4 This file is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 This file 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
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
30 /* This override can only support a limited number of arguments. */
33 /* A parsed directive. */
36 bool needs_long_double
;
37 const char *conversion_ptr
;
41 /* A parsed format string. */
45 directive_t dir
[MAX_ARGS
];
49 /* Parses a monetary format string.
50 Returns 0 and fills *DIRECTIVESP if valid.
51 Returns -1 if invalid. */
53 fmon_parse (const char *format
, directives_t
*directivesp
)
56 const char *cp
= format
;
63 while (*cp
== '=' || *cp
== '^' || *cp
== '+' || *cp
== '('
64 || *cp
== '!' || *cp
== '-')
74 /* Parse field width. */
75 while (*cp
>= '0' && *cp
<= '9')
77 /* Parse left precision. */
81 while (*cp
>= '0' && *cp
<= '9')
84 /* Parse right precision. */
88 while (*cp
>= '0' && *cp
<= '9')
91 /* Now comes the conversion specifier. */
94 if (count
== MAX_ARGS
)
95 /* Too many arguments. */
98 /* glibc supports an 'L' modifier before the conversion specifier. */
102 directivesp
->dir
[count
].needs_long_double
= true;
105 directivesp
->dir
[count
].needs_long_double
= false;
106 if (!(*cp
== 'i' || *cp
== 'n'))
108 directivesp
->dir
[count
].conversion_ptr
= cp
;
115 directivesp
->count
= count
;
120 rpl_strfmon_l (char *s
, size_t maxsize
, locale_t locale
, const char *format
, ...)
122 /* Work around glibc 2.23 bug
123 <https://sourceware.org/bugzilla/show_bug.cgi?id=19633>. */
125 locale_t orig_locale
;
126 directives_t directives
;
129 orig_locale
= uselocale ((locale_t
)0);
131 if (uselocale (locale
) == (locale_t
)0)
135 /* The format string may consume 'double' or 'long double' arguments.
136 In order not to have to link with libffcall or libffi, convert all
137 arguments to 'long double', and use a modified format string that
138 requests 'long double' arguments. But since 'long double' arguments
139 are only supported on glibc, do so only if the original format string
140 consumes at least one 'long double' argument. */
141 if (fmon_parse (format
, &directives
) < 0)
148 bool use_long_double
;
151 use_long_double
= false;
152 for (i
= 0; i
< directives
.count
; i
++)
153 if (directives
.dir
[i
].needs_long_double
)
155 use_long_double
= true;
159 va_start (argptr
, format
);
165 /* Allocate room for the modified format string. */
166 ld_format
= (char *) malloc (strlen (format
) + directives
.count
+ 1);
167 if (ld_format
== NULL
)
174 long double args
[MAX_ARGS
];
176 /* Create the modified format string. */
178 const char *p
= format
;
179 char *dest
= ld_format
;
180 for (i
= 0; i
< directives
.count
; i
++)
182 const char *q
= directives
.dir
[i
].conversion_ptr
;
183 memcpy (dest
, p
, q
- p
);
185 if (!directives
.dir
[i
].needs_long_double
)
192 /* Set up arguments array. */
193 for (i
= 0; i
< directives
.count
; i
++)
194 args
[i
] = (directives
.dir
[i
].needs_long_double
195 ? va_arg (argptr
, long double)
196 : (long double) va_arg (argptr
, double));
197 /* Avoid uninitialized memory references. */
198 for (; i
< MAX_ARGS
; i
++)
201 result
= strfmon_l (s
, maxsize
, locale
, ld_format
,
202 args
[0], args
[1], args
[2], args
[3], args
[4],
203 args
[5], args
[6], args
[7], args
[8], args
[9],
204 args
[10], args
[11], args
[12], args
[13],
212 double args
[MAX_ARGS
];
214 /* Set up arguments array. */
215 for (i
= 0; i
< directives
.count
; i
++)
216 args
[i
] = va_arg (argptr
, double);
217 /* Avoid uninitialized memory references. */
218 for (; i
< MAX_ARGS
; i
++)
221 result
= strfmon_l (s
, maxsize
, locale
, format
,
222 args
[0], args
[1], args
[2], args
[3], args
[4],
223 args
[5], args
[6], args
[7], args
[8], args
[9],
224 args
[10], args
[11], args
[12], args
[13], args
[14],
231 if (uselocale (orig_locale
) == (locale_t
)0)