1 /* Formatting a monetary value according to the given locale.
2 Copyright (C) 1996,1997,2002,2004,2006,2009 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, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 #include "../libio/libioP.h"
27 #include "../libio/strfile.h"
32 #include "../locale/localeinfo.h"
35 #define out_char(Ch) \
37 if (dest >= s + maxsize - 1) \
39 __set_errno (E2BIG); \
46 #define out_string(String) \
48 const char *_s = (String); \
53 #define out_nstring(String, N) \
56 const char *_s = (String); \
61 #define to_digit(Ch) ((Ch) - '0')
64 /* We use this code also for the extended locale handling where the
65 function gets as an additional argument the locale which has to be
66 used. To access the values we have to redefine the _NL_CURRENT
69 #define _NL_CURRENT(category, item) \
70 (current->values[_NL_ITEM_INDEX (item)].string)
72 extern int __printf_fp (FILE *, const struct printf_info
*,
74 libc_hidden_proto (__printf_fp
)
75 /* This function determines the number of digit groups in the output.
76 The definition is in printf_fp.c. */
77 extern unsigned int __guess_grouping (unsigned int intdig_max
,
78 const char *grouping
, wchar_t sepchar
);
81 /* We have to overcome some problems with this implementation. On the
82 one hand the strfmon() function is specified in XPG4 and of course
83 it has to follow this. But on the other hand POSIX.2 specifies
84 some information in the LC_MONETARY category which should be used,
85 too. Some of the information contradicts the information which can
86 be specified in format string. */
88 __vstrfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
,
91 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
93 struct printf_info info
;
94 char *dest
; /* Pointer so copy the output. */
95 const char *fmt
; /* Pointer that walks through format. */
100 /* Loop through the format-string. */
103 /* The floating-point value to output. */
107 __long_double_t ldbl
;
111 int print_curr_symbol
;
125 int other_sep_by_space
;
127 int other_cs_precedes
;
128 const char *sign_string
;
129 const char *other_sign_string
;
131 const char *currency_symbol
;
132 size_t currency_symbol_len
;
138 /* Process all character which do not introduce a format
146 /* "%%" means a single '%' character. */
154 /* Defaults for formatting. */
155 int_format
= 0; /* Use international curr. symbol */
156 print_curr_symbol
= 1; /* Print the currency symbol. */
157 left_prec
= -1; /* No left precision specified. */
158 right_prec
= -1; /* No right precision specified. */
159 group
= 1; /* Print digits grouped. */
160 pad
= ' '; /* Fill character is <SP>. */
161 is_long_double
= 0; /* Double argument by default. */
162 p_sign_posn
= -1; /* This indicates whether the */
163 n_sign_posn
= -1; /* '(' flag is given. */
164 width
= -1; /* No width specified so far. */
165 left
= 0; /* Right justified by default. */
167 /* Parse group characters. */
172 case '=': /* Set fill character. */
177 __set_errno (EINVAL
);
181 case '^': /* Don't group digits. */
184 case '+': /* Use +/- for sign of number. */
185 if (n_sign_posn
!= -1)
187 __set_errno (EINVAL
);
190 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
191 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
193 case '(': /* Use ( ) for negative sign. */
194 if (n_sign_posn
!= -1)
196 __set_errno (EINVAL
);
202 case '!': /* Don't print the currency symbol. */
203 print_curr_symbol
= 0;
205 case '-': /* Print left justified. */
209 /* Will stop the loop. */;
216 /* Parse field width. */
217 width
= to_digit (*fmt
);
219 while (isdigit (*++fmt
))
221 int val
= to_digit (*fmt
);
223 if (width
> LONG_MAX
/ 10
224 || (width
== LONG_MAX
&& val
> LONG_MAX
% 10))
230 width
= width
* 10 + val
;
233 /* If we don't have enough room for the demanded width we
234 can stop now and return an error. */
235 if (width
>= maxsize
- (dest
- s
))
242 /* Recognize left precision. */
245 if (!isdigit (*++fmt
))
247 __set_errno (EINVAL
);
250 left_prec
= to_digit (*fmt
);
252 while (isdigit (*++fmt
))
255 left_prec
+= to_digit (*fmt
);
259 /* Recognize right precision. */
262 if (!isdigit (*++fmt
))
264 __set_errno (EINVAL
);
267 right_prec
= to_digit (*fmt
);
269 while (isdigit (*++fmt
))
272 right_prec
+= to_digit (*fmt
);
276 /* Handle modifier. This is an extension. */
284 /* Handle format specifier. */
288 case 'i': { /* Use international currency symbol. */
289 const char *int_curr_symbol
;
291 int_curr_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
292 strncpy(int_symbol
, int_curr_symbol
, 3);
293 int_symbol
[3] = '\0';
295 currency_symbol_len
= 3;
296 currency_symbol
= &int_symbol
[0];
297 space_char
= int_curr_symbol
[3];
301 case 'n': /* Use national currency symbol. */
302 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
303 currency_symbol_len
= strlen (currency_symbol
);
307 default: /* Any unrecognized format is an error. */
308 __set_errno (EINVAL
);
312 /* If not specified by the format string now find the values for
313 the format specification. */
314 if (p_sign_posn
== -1)
315 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SIGN_POSN
: P_SIGN_POSN
);
316 if (n_sign_posn
== -1)
317 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SIGN_POSN
: N_SIGN_POSN
);
319 if (right_prec
== -1)
321 right_prec
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_FRAC_DIGITS
: FRAC_DIGITS
);
323 if (right_prec
== CHAR_MAX
)
327 /* If we have to print the digits grouped determine how many
328 extra characters this means. */
329 if (group
&& left_prec
!= -1)
330 left_prec
+= __guess_grouping (left_prec
,
331 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
332 *_NL_CURRENT (LC_MONETARY
,
335 /* Now it's time to get the value. */
336 if (is_long_double
== 1)
338 fpnum
.ldbl
= va_arg (ap
, long double);
339 is_negative
= fpnum
.ldbl
< 0;
341 fpnum
.ldbl
= -fpnum
.ldbl
;
345 fpnum
.dbl
= va_arg (ap
, double);
346 is_negative
= fpnum
.dbl
< 0;
348 fpnum
.dbl
= -fpnum
.dbl
;
351 /* We now know the sign of the value and can determine the format. */
354 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
355 /* If the locale does not specify a character for the
356 negative sign we use a '-'. */
357 if (*sign_string
== '\0')
358 sign_string
= (const char *) "-";
359 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
360 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
361 sign_posn
= n_sign_posn
;
363 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
364 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
365 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
366 other_sign_posn
= p_sign_posn
;
370 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
371 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
372 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
373 sign_posn
= p_sign_posn
;
375 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
376 if (*other_sign_string
== '\0')
377 other_sign_string
= (const char *) "-";
378 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
379 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
380 other_sign_posn
= n_sign_posn
;
383 /* Set default values for unspecified information. */
384 if (cs_precedes
!= 0)
386 if (other_cs_precedes
!= 0)
387 other_cs_precedes
= 1;
388 if (sep_by_space
== CHAR_MAX
)
390 if (other_sep_by_space
== CHAR_MAX
)
391 other_sep_by_space
= 0;
392 if (sign_posn
== CHAR_MAX
)
394 if (other_sign_posn
== CHAR_MAX
)
397 /* Check for degenerate cases */
398 if (sep_by_space
== 2)
400 if (sign_posn
== 0 ||
401 (sign_posn
== 1 && !cs_precedes
) ||
402 (sign_posn
== 2 && cs_precedes
))
403 /* sign and symbol are not adjacent, so no separator */
406 if (other_sep_by_space
== 2)
408 if (other_sign_posn
== 0 ||
409 (other_sign_posn
== 1 && !other_cs_precedes
) ||
410 (other_sign_posn
== 2 && other_cs_precedes
))
411 /* sign and symbol are not adjacent, so no separator */
412 other_sep_by_space
= 0;
415 /* Set the left precision and padding needed for alignment */
423 /* Set left_pad to number of spaces needed to align positive
424 and negative formats */
427 int other_left_bytes
= 0;
429 /* Work out number of bytes for currency string and separator
430 preceding the value */
433 left_bytes
+= currency_symbol_len
;
434 if (sep_by_space
!= 0)
438 if (other_cs_precedes
)
440 other_left_bytes
+= currency_symbol_len
;
441 if (other_sep_by_space
!= 0)
445 /* Work out number of bytes for the sign (or left parenthesis)
446 preceding the value */
447 if (sign_posn
== 0 && is_negative
)
449 else if (sign_posn
== 1)
450 left_bytes
+= strlen (sign_string
);
451 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
452 left_bytes
+= strlen (sign_string
);
454 if (other_sign_posn
== 0 && !is_negative
)
456 else if (other_sign_posn
== 1)
457 other_left_bytes
+= strlen (other_sign_string
);
458 else if (other_cs_precedes
&&
459 (other_sign_posn
== 3 || other_sign_posn
== 4))
460 other_left_bytes
+= strlen (other_sign_string
);
462 /* Compare the number of bytes preceding the value for
463 each format, and set the padding accordingly */
464 if (other_left_bytes
> left_bytes
)
465 left_pad
= other_left_bytes
- left_bytes
;
470 /* Perhaps we'll someday make these things configurable so
471 better start using symbolic names now. */
472 #define left_paren '('
473 #define right_paren ')'
475 startp
= dest
; /* Remember start so we can compute length. */
477 while (left_pad
-- > 0)
480 if (sign_posn
== 0 && is_negative
)
481 out_char (left_paren
);
485 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
488 out_string (sign_string
);
489 if (sep_by_space
== 2)
493 if (print_curr_symbol
)
494 out_string (currency_symbol
);
498 if (print_curr_symbol
&& sep_by_space
== 2)
499 out_char (space_char
);
500 out_string (sign_string
);
501 if (sep_by_space
== 1)
502 /* POSIX.2 and SUS are not clear on this case, but C99
503 says a space follows the adjacent-symbol-and-sign */
507 if (print_curr_symbol
&& sep_by_space
== 1)
508 out_char (space_char
);
511 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
512 && sign_posn
!= 4 && sign_posn
!= 5)
513 out_string (sign_string
);
515 /* Print the number. */
517 f
._sbf
._f
._lock
= NULL
;
519 INTUSE(_IO_init
) (&f
._sbf
._f
, 0);
520 _IO_JUMPS (&f
._sbf
) = &_IO_str_jumps
;
521 INTUSE(_IO_str_init_static
) (&f
, dest
,
522 (s
+ maxsize
) - dest
, dest
);
523 /* We clear the last available byte so we can find out whether
524 the numeric representation is too long. */
525 s
[maxsize
- 1] = '\0';
527 memset (&info
, '\0', sizeof (info
));
528 info
.prec
= right_prec
;
529 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
531 info
.is_long_double
= is_long_double
;
534 info
.extra
= 1; /* This means use values from LC_MONETARY. */
537 done
= __printf_fp (&f
._sbf
._f
, &info
, &ptr
);
541 if (s
[maxsize
- 1] != '\0')
553 if (sep_by_space
== 1)
555 out_string (sign_string
);
558 if (print_curr_symbol
)
560 if ((sign_posn
== 3 && sep_by_space
== 2)
561 || (sign_posn
== 4 && sep_by_space
== 1)
562 || (sign_posn
== 2 && sep_by_space
== 1)
563 || (sign_posn
== 1 && sep_by_space
== 1)
564 || (sign_posn
== 0 && sep_by_space
== 1))
565 out_char (space_char
);
566 out_nstring (currency_symbol
, currency_symbol_len
);
571 if (sep_by_space
== 2)
573 out_string (sign_string
);
579 if (sep_by_space
== 2)
581 out_string (sign_string
);
584 if (sign_posn
== 0 && is_negative
)
585 out_char (right_paren
);
587 /* Now test whether the output width is filled. */
588 if (dest
- startp
< width
)
591 /* We simply have to fill using spaces. */
594 while (dest
- startp
< width
);
597 long int dist
= width
- (dest
- startp
);
598 for (char *cp
= dest
- 1; cp
>= startp
; --cp
)
604 startp
[--dist
] = ' ';
610 /* Terminate the string. */
617 ___strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
621 va_start (ap
, format
);
623 ssize_t res
= __vstrfmon_l (s
, maxsize
, loc
, format
, ap
);
629 ldbl_strong_alias (___strfmon_l
, __strfmon_l
)
630 ldbl_weak_alias (___strfmon_l
, strfmon_l
)