2 Copyright (C) 2017-2020 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
9 This program 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 General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, see <https://www.gnu.org/licenses/>. */
31 /* This override can only support a limited number of arguments. */
34 /* A parsed directive. */
37 bool needs_long_double
;
38 const char *conversion_ptr
;
42 /* A parsed format string. */
46 directive_t dir
[MAX_ARGS
];
50 /* Parses a monetary format string.
51 Returns 0 and fills *DIRECTIVESP if valid.
52 Returns -1 if invalid. */
54 fmon_parse (const char *format
, directives_t
*directivesp
)
57 const char *cp
= format
;
64 while (*cp
== '=' || *cp
== '^' || *cp
== '+' || *cp
== '('
65 || *cp
== '!' || *cp
== '-')
75 /* Parse field width. */
76 while (*cp
>= '0' && *cp
<= '9')
78 /* Parse left precision. */
82 while (*cp
>= '0' && *cp
<= '9')
85 /* Parse right precision. */
89 while (*cp
>= '0' && *cp
<= '9')
92 /* Now comes the conversion specifier. */
95 if (count
== MAX_ARGS
)
96 /* Too many arguments. */
99 /* glibc supports an 'L' modifier before the conversion specifier. */
103 directivesp
->dir
[count
].needs_long_double
= true;
106 directivesp
->dir
[count
].needs_long_double
= false;
107 if (!(*cp
== 'i' || *cp
== 'n'))
109 directivesp
->dir
[count
].conversion_ptr
= cp
;
116 directivesp
->count
= count
;
121 rpl_strfmon_l (char *s
, size_t maxsize
, locale_t locale
, const char *format
, ...)
123 /* Work around glibc 2.23 bug
124 <https://sourceware.org/bugzilla/show_bug.cgi?id=19633>. */
126 locale_t orig_locale
;
127 directives_t directives
;
130 orig_locale
= uselocale ((locale_t
)0);
132 if (uselocale (locale
) == (locale_t
)0)
136 /* The format string may consume 'double' or 'long double' arguments.
137 In order not to have to link with libffcall or libffi, convert all
138 arguments to 'long double', and use a modified format string that
139 requests 'long double' arguments. But since 'long double' arguments
140 are only supported on glibc, do so only if the original format string
141 consumes at least one 'long double' argument. */
142 if (fmon_parse (format
, &directives
) < 0)
149 bool use_long_double
;
152 use_long_double
= false;
153 for (i
= 0; i
< directives
.count
; i
++)
154 if (directives
.dir
[i
].needs_long_double
)
156 use_long_double
= true;
160 va_start (argptr
, format
);
166 /* Allocate room for the modified format string. */
167 ld_format
= (char *) malloc (strlen (format
) + directives
.count
+ 1);
168 if (ld_format
== NULL
)
175 long double args
[MAX_ARGS
];
177 /* Create the modified format string. */
179 const char *p
= format
;
180 char *dest
= ld_format
;
181 for (i
= 0; i
< directives
.count
; i
++)
183 const char *q
= directives
.dir
[i
].conversion_ptr
;
184 memcpy (dest
, p
, q
- p
);
186 if (!directives
.dir
[i
].needs_long_double
)
193 /* Set up arguments array. */
194 for (i
= 0; i
< directives
.count
; i
++)
195 args
[i
] = (directives
.dir
[i
].needs_long_double
196 ? va_arg (argptr
, long double)
197 : (long double) va_arg (argptr
, double));
198 /* Avoid uninitialized memory references. */
199 for (; i
< MAX_ARGS
; i
++)
202 result
= strfmon_l (s
, maxsize
, locale
, ld_format
,
203 args
[0], args
[1], args
[2], args
[3], args
[4],
204 args
[5], args
[6], args
[7], args
[8], args
[9],
205 args
[10], args
[11], args
[12], args
[13],
213 double args
[MAX_ARGS
];
215 /* Set up arguments array. */
216 for (i
= 0; i
< directives
.count
; i
++)
217 args
[i
] = va_arg (argptr
, double);
218 /* Avoid uninitialized memory references. */
219 for (; i
< MAX_ARGS
; i
++)
222 result
= strfmon_l (s
, maxsize
, locale
, format
,
223 args
[0], args
[1], args
[2], args
[3], args
[4],
224 args
[5], args
[6], args
[7], args
[8], args
[9],
225 args
[10], args
[11], args
[12], args
[13], args
[14],
232 if (uselocale (orig_locale
) == (locale_t
)0)