1 /* Formatting a monetary value according to the given locale.
2 Copyright (C) 1996-2016 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
25 #include "../libio/libioP.h"
26 #include "../libio/strfile.h"
31 #include "../locale/localeinfo.h"
34 #define out_char(Ch) \
36 if (dest >= s + maxsize - 1) \
38 __set_errno (E2BIG); \
45 #define out_string(String) \
47 const char *_s = (String); \
52 #define out_nstring(String, N) \
55 const char *_s = (String); \
60 #define to_digit(Ch) ((Ch) - '0')
63 /* We use this code also for the extended locale handling where the
64 function gets as an additional argument the locale which has to be
65 used. To access the values we have to redefine the _NL_CURRENT
68 #define _NL_CURRENT(category, item) \
69 (current->values[_NL_ITEM_INDEX (item)].string)
71 extern int __printf_fp (FILE *, const struct printf_info
*,
73 libc_hidden_proto (__printf_fp
)
74 /* This function determines the number of digit groups in the output.
75 The definition is in printf_fp.c. */
76 extern unsigned int __guess_grouping (unsigned int intdig_max
,
77 const char *grouping
, wchar_t sepchar
);
80 /* We have to overcome some problems with this implementation. On the
81 one hand the strfmon() function is specified in XPG4 and of course
82 it has to follow this. But on the other hand POSIX.2 specifies
83 some information in the LC_MONETARY category which should be used,
84 too. Some of the information contradicts the information which can
85 be specified in format string. */
87 __vstrfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
,
90 struct __locale_data
*current
= loc
->__locales
[LC_MONETARY
];
92 struct printf_info info
;
93 char *dest
; /* Pointer so copy the output. */
94 const char *fmt
; /* Pointer that walks through format. */
99 /* Loop through the format-string. */
102 /* The floating-point value to output. */
106 __long_double_t ldbl
;
110 int print_curr_symbol
;
124 int other_sep_by_space
;
126 int other_cs_precedes
;
127 const char *sign_string
;
128 const char *other_sign_string
;
130 const char *currency_symbol
;
131 size_t currency_symbol_len
;
137 /* Process all character which do not introduce a format
145 /* "%%" means a single '%' character. */
153 /* Defaults for formatting. */
154 int_format
= 0; /* Use international curr. symbol */
155 print_curr_symbol
= 1; /* Print the currency symbol. */
156 left_prec
= -1; /* No left precision specified. */
157 right_prec
= -1; /* No right precision specified. */
158 group
= 1; /* Print digits grouped. */
159 pad
= ' '; /* Fill character is <SP>. */
160 is_long_double
= 0; /* Double argument by default. */
161 p_sign_posn
= -2; /* This indicates whether the */
162 n_sign_posn
= -2; /* '(' flag is given. */
163 width
= -1; /* No width specified so far. */
164 left
= 0; /* Right justified by default. */
166 /* Parse group characters. */
171 case '=': /* Set fill character. */
176 __set_errno (EINVAL
);
180 case '^': /* Don't group digits. */
183 case '+': /* Use +/- for sign of number. */
184 if (n_sign_posn
!= -2)
186 __set_errno (EINVAL
);
189 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
190 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
192 case '(': /* Use ( ) for negative sign. */
193 if (n_sign_posn
!= -2)
195 __set_errno (EINVAL
);
201 case '!': /* Don't print the currency symbol. */
202 print_curr_symbol
= 0;
204 case '-': /* Print left justified. */
208 /* Will stop the loop. */;
215 /* Parse field width. */
216 width
= to_digit (*fmt
);
218 while (isdigit (*++fmt
))
220 int val
= to_digit (*fmt
);
222 if (width
> LONG_MAX
/ 10
223 || (width
== LONG_MAX
&& val
> LONG_MAX
% 10))
229 width
= width
* 10 + val
;
232 /* If we don't have enough room for the demanded width we
233 can stop now and return an error. */
234 if (width
>= maxsize
- (dest
- s
))
241 /* Recognize left precision. */
244 if (!isdigit (*++fmt
))
246 __set_errno (EINVAL
);
249 left_prec
= to_digit (*fmt
);
251 while (isdigit (*++fmt
))
254 left_prec
+= to_digit (*fmt
);
258 /* Recognize right precision. */
261 if (!isdigit (*++fmt
))
263 __set_errno (EINVAL
);
266 right_prec
= to_digit (*fmt
);
268 while (isdigit (*++fmt
))
271 right_prec
+= to_digit (*fmt
);
275 /* Handle modifier. This is an extension. */
283 /* Handle format specifier. */
287 case 'i': { /* Use international currency symbol. */
288 const char *int_curr_symbol
;
290 int_curr_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
291 strncpy(int_symbol
, int_curr_symbol
, 3);
292 int_symbol
[3] = '\0';
294 currency_symbol_len
= 3;
295 currency_symbol
= &int_symbol
[0];
296 space_char
= int_curr_symbol
[3];
300 case 'n': /* Use national currency symbol. */
301 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
302 currency_symbol_len
= strlen (currency_symbol
);
306 default: /* Any unrecognized format is an error. */
307 __set_errno (EINVAL
);
311 /* If not specified by the format string now find the values for
312 the format specification. */
313 if (p_sign_posn
== -2)
314 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SIGN_POSN
: P_SIGN_POSN
);
315 if (n_sign_posn
== -2)
316 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SIGN_POSN
: N_SIGN_POSN
);
318 if (right_prec
== -1)
320 right_prec
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_FRAC_DIGITS
: FRAC_DIGITS
);
322 if (right_prec
== '\377')
326 /* If we have to print the digits grouped determine how many
327 extra characters this means. */
328 if (group
&& left_prec
!= -1)
329 left_prec
+= __guess_grouping (left_prec
,
330 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
331 *_NL_CURRENT (LC_MONETARY
,
334 /* Now it's time to get the value. */
335 if (is_long_double
== 1)
337 fpnum
.ldbl
= va_arg (ap
, long double);
338 is_negative
= fpnum
.ldbl
< 0;
340 fpnum
.ldbl
= -fpnum
.ldbl
;
344 fpnum
.dbl
= va_arg (ap
, double);
345 is_negative
= fpnum
.dbl
< 0;
347 fpnum
.dbl
= -fpnum
.dbl
;
350 /* We now know the sign of the value and can determine the format. */
353 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
354 /* If the locale does not specify a character for the
355 negative sign we use a '-'. */
356 if (*sign_string
== '\0')
357 sign_string
= (const char *) "-";
358 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
359 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
360 sign_posn
= n_sign_posn
;
362 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
363 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
364 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
365 other_sign_posn
= p_sign_posn
;
369 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
370 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
371 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
372 sign_posn
= p_sign_posn
;
374 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
375 if (*other_sign_string
== '\0')
376 other_sign_string
= (const char *) "-";
377 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
378 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
379 other_sign_posn
= n_sign_posn
;
382 /* Set default values for unspecified information. */
383 if (cs_precedes
!= 0)
385 if (other_cs_precedes
!= 0)
386 other_cs_precedes
= 1;
387 if (sep_by_space
== '\377')
389 if (other_sep_by_space
== '\377')
390 other_sep_by_space
= 0;
391 if (sign_posn
== '\377')
393 if (other_sign_posn
== '\377')
396 /* Check for degenerate cases */
397 if (sep_by_space
== 2)
399 if (sign_posn
== 0 ||
400 (sign_posn
== 1 && !cs_precedes
) ||
401 (sign_posn
== 2 && cs_precedes
))
402 /* sign and symbol are not adjacent, so no separator */
405 if (other_sep_by_space
== 2)
407 if (other_sign_posn
== 0 ||
408 (other_sign_posn
== 1 && !other_cs_precedes
) ||
409 (other_sign_posn
== 2 && other_cs_precedes
))
410 /* sign and symbol are not adjacent, so no separator */
411 other_sep_by_space
= 0;
414 /* Set the left precision and padding needed for alignment */
422 /* Set left_pad to number of spaces needed to align positive
423 and negative formats */
426 int other_left_bytes
= 0;
428 /* Work out number of bytes for currency string and separator
429 preceding the value */
432 left_bytes
+= currency_symbol_len
;
433 if (sep_by_space
!= 0)
437 if (other_cs_precedes
)
439 other_left_bytes
+= currency_symbol_len
;
440 if (other_sep_by_space
!= 0)
444 /* Work out number of bytes for the sign (or left parenthesis)
445 preceding the value */
446 if (sign_posn
== 0 && is_negative
)
448 else if (sign_posn
== 1)
449 left_bytes
+= strlen (sign_string
);
450 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
451 left_bytes
+= strlen (sign_string
);
453 if (other_sign_posn
== 0 && !is_negative
)
455 else if (other_sign_posn
== 1)
456 other_left_bytes
+= strlen (other_sign_string
);
457 else if (other_cs_precedes
&&
458 (other_sign_posn
== 3 || other_sign_posn
== 4))
459 other_left_bytes
+= strlen (other_sign_string
);
461 /* Compare the number of bytes preceding the value for
462 each format, and set the padding accordingly */
463 if (other_left_bytes
> left_bytes
)
464 left_pad
= other_left_bytes
- left_bytes
;
469 /* Perhaps we'll someday make these things configurable so
470 better start using symbolic names now. */
471 #define left_paren '('
472 #define right_paren ')'
474 startp
= dest
; /* Remember start so we can compute length. */
476 while (left_pad
-- > 0)
479 if (sign_posn
== 0 && is_negative
)
480 out_char (left_paren
);
484 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
487 out_string (sign_string
);
488 if (sep_by_space
== 2)
492 if (print_curr_symbol
)
493 out_string (currency_symbol
);
497 if (print_curr_symbol
&& sep_by_space
== 2)
498 out_char (space_char
);
499 out_string (sign_string
);
500 if (sep_by_space
== 1)
501 /* POSIX.2 and SUS are not clear on this case, but C99
502 says a space follows the adjacent-symbol-and-sign */
506 if (print_curr_symbol
&& sep_by_space
== 1)
507 out_char (space_char
);
510 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
511 && sign_posn
!= 4 && sign_posn
!= 5)
512 out_string (sign_string
);
514 /* Print the number. */
516 f
._sbf
._f
._lock
= NULL
;
518 _IO_init (&f
._sbf
._f
, 0);
519 _IO_JUMPS (&f
._sbf
) = &_IO_str_jumps
;
520 _IO_str_init_static_internal (&f
, dest
, (s
+ maxsize
) - dest
, dest
);
521 /* We clear the last available byte so we can find out whether
522 the numeric representation is too long. */
523 s
[maxsize
- 1] = '\0';
525 memset (&info
, '\0', sizeof (info
));
526 info
.prec
= right_prec
;
527 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
529 info
.is_long_double
= is_long_double
;
532 info
.extra
= 1; /* This means use values from LC_MONETARY. */
535 done
= __printf_fp (&f
._sbf
._f
, &info
, &ptr
);
539 if (s
[maxsize
- 1] != '\0')
551 if (sep_by_space
== 1)
553 out_string (sign_string
);
556 if (print_curr_symbol
)
558 if ((sign_posn
== 3 && sep_by_space
== 2)
559 || (sign_posn
== 4 && sep_by_space
== 1)
560 || (sign_posn
== 2 && sep_by_space
== 1)
561 || (sign_posn
== 1 && sep_by_space
== 1)
562 || (sign_posn
== 0 && sep_by_space
== 1))
563 out_char (space_char
);
564 out_nstring (currency_symbol
, currency_symbol_len
);
569 if (sep_by_space
== 2)
571 out_string (sign_string
);
577 if (sep_by_space
== 2)
579 out_string (sign_string
);
582 if (sign_posn
== 0 && is_negative
)
583 out_char (right_paren
);
585 /* Now test whether the output width is filled. */
586 if (dest
- startp
< width
)
589 /* We simply have to fill using spaces. */
592 while (dest
- startp
< width
);
595 long int dist
= width
- (dest
- startp
);
596 for (char *cp
= dest
- 1; cp
>= startp
; --cp
)
602 startp
[--dist
] = ' ';
608 /* Terminate the string. */
615 ___strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
619 va_start (ap
, format
);
621 ssize_t res
= __vstrfmon_l (s
, maxsize
, loc
, format
, ap
);
627 ldbl_strong_alias (___strfmon_l
, __strfmon_l
)
628 ldbl_weak_alias (___strfmon_l
, strfmon_l
)