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
];
96 struct printf_info info
;
97 char *dest
; /* Pointer so copy the output. */
98 const char *fmt
; /* Pointer that walks through format. */
103 /* Loop through the format-string. */
106 /* The floating-point value to output. */
110 __long_double_t ldbl
;
114 int print_curr_symbol
;
128 int other_sep_by_space
;
130 int other_cs_precedes
;
131 const char *sign_string
;
132 const char *other_sign_string
;
134 const char *currency_symbol
;
135 size_t currency_symbol_len
;
141 /* Process all character which do not introduce a format
149 /* "%%" means a single '%' character. */
157 /* Defaults for formatting. */
158 int_format
= 0; /* Use international curr. symbol */
159 print_curr_symbol
= 1; /* Print the currency symbol. */
160 left_prec
= -1; /* No left precision specified. */
161 right_prec
= -1; /* No right precision specified. */
162 group
= 1; /* Print digits grouped. */
163 pad
= ' '; /* Fill character is <SP>. */
164 is_long_double
= 0; /* Double argument by default. */
165 p_sign_posn
= -1; /* This indicates whether the */
166 n_sign_posn
= -1; /* '(' flag is given. */
167 width
= -1; /* No width specified so far. */
168 left
= 0; /* Right justified by default. */
170 /* Parse group characters. */
175 case '=': /* Set fill character. */
180 __set_errno (EINVAL
);
184 case '^': /* Don't group digits. */
187 case '+': /* Use +/- for sign of number. */
188 if (n_sign_posn
!= -1)
190 __set_errno (EINVAL
);
193 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
194 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
196 case '(': /* Use ( ) for negative sign. */
197 if (n_sign_posn
!= -1)
199 __set_errno (EINVAL
);
205 case '!': /* Don't print the currency symbol. */
206 print_curr_symbol
= 0;
208 case '-': /* Print left justified. */
212 /* Will stop the loop. */;
219 /* Parse field width. */
220 width
= to_digit (*fmt
);
222 while (isdigit (*++fmt
))
224 int val
= to_digit (*fmt
);
226 if (width
> LONG_MAX
/ 10
227 || (width
== LONG_MAX
&& val
> LONG_MAX
% 10))
233 width
= width
* 10 + val
;
236 /* If we don't have enough room for the demanded width we
237 can stop now and return an error. */
238 if (width
>= maxsize
- (dest
- s
))
245 /* Recognize left precision. */
248 if (!isdigit (*++fmt
))
250 __set_errno (EINVAL
);
253 left_prec
= to_digit (*fmt
);
255 while (isdigit (*++fmt
))
258 left_prec
+= to_digit (*fmt
);
262 /* Recognize right precision. */
265 if (!isdigit (*++fmt
))
267 __set_errno (EINVAL
);
270 right_prec
= to_digit (*fmt
);
272 while (isdigit (*++fmt
))
275 right_prec
+= to_digit (*fmt
);
279 /* Handle modifier. This is an extension. */
287 /* Handle format specifier. */
291 case 'i': { /* Use international currency symbol. */
292 const char *int_curr_symbol
;
294 int_curr_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
295 strncpy(int_symbol
, int_curr_symbol
, 3);
296 int_symbol
[3] = '\0';
298 currency_symbol_len
= 3;
299 currency_symbol
= &int_symbol
[0];
300 space_char
= int_curr_symbol
[3];
304 case 'n': /* Use national currency symbol. */
305 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
306 currency_symbol_len
= strlen (currency_symbol
);
310 default: /* Any unrecognized format is an error. */
311 __set_errno (EINVAL
);
315 /* If not specified by the format string now find the values for
316 the format specification. */
317 if (p_sign_posn
== -1)
318 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SIGN_POSN
: P_SIGN_POSN
);
319 if (n_sign_posn
== -1)
320 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SIGN_POSN
: N_SIGN_POSN
);
322 if (right_prec
== -1)
324 right_prec
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_FRAC_DIGITS
: FRAC_DIGITS
);
326 if (right_prec
== CHAR_MAX
)
330 /* If we have to print the digits grouped determine how many
331 extra characters this means. */
332 if (group
&& left_prec
!= -1)
333 left_prec
+= __guess_grouping (left_prec
,
334 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
335 *_NL_CURRENT (LC_MONETARY
,
338 /* Now it's time to get the value. */
339 if (is_long_double
== 1)
341 fpnum
.ldbl
= va_arg (ap
, long double);
342 is_negative
= fpnum
.ldbl
< 0;
344 fpnum
.ldbl
= -fpnum
.ldbl
;
348 fpnum
.dbl
= va_arg (ap
, double);
349 is_negative
= fpnum
.dbl
< 0;
351 fpnum
.dbl
= -fpnum
.dbl
;
354 /* We now know the sign of the value and can determine the format. */
357 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
358 /* If the locale does not specify a character for the
359 negative sign we use a '-'. */
360 if (*sign_string
== '\0')
361 sign_string
= (const char *) "-";
362 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
363 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
364 sign_posn
= n_sign_posn
;
366 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
367 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
368 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
369 other_sign_posn
= p_sign_posn
;
373 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
374 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
375 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
376 sign_posn
= p_sign_posn
;
378 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
379 if (*other_sign_string
== '\0')
380 other_sign_string
= (const char *) "-";
381 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
382 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
383 other_sign_posn
= n_sign_posn
;
386 /* Set default values for unspecified information. */
387 if (cs_precedes
!= 0)
389 if (other_cs_precedes
!= 0)
390 other_cs_precedes
= 1;
391 if (sep_by_space
== CHAR_MAX
)
393 if (other_sep_by_space
== CHAR_MAX
)
394 other_sep_by_space
= 0;
395 if (sign_posn
== CHAR_MAX
)
397 if (other_sign_posn
== CHAR_MAX
)
400 /* Check for degenerate cases */
401 if (sep_by_space
== 2)
403 if (sign_posn
== 0 ||
404 (sign_posn
== 1 && !cs_precedes
) ||
405 (sign_posn
== 2 && cs_precedes
))
406 /* sign and symbol are not adjacent, so no separator */
409 if (other_sep_by_space
== 2)
411 if (other_sign_posn
== 0 ||
412 (other_sign_posn
== 1 && !other_cs_precedes
) ||
413 (other_sign_posn
== 2 && other_cs_precedes
))
414 /* sign and symbol are not adjacent, so no separator */
415 other_sep_by_space
= 0;
418 /* Set the left precision and padding needed for alignment */
426 /* Set left_pad to number of spaces needed to align positive
427 and negative formats */
430 int other_left_bytes
= 0;
432 /* Work out number of bytes for currency string and separator
433 preceding the value */
436 left_bytes
+= currency_symbol_len
;
437 if (sep_by_space
!= 0)
441 if (other_cs_precedes
)
443 other_left_bytes
+= currency_symbol_len
;
444 if (other_sep_by_space
!= 0)
448 /* Work out number of bytes for the sign (or left parenthesis)
449 preceding the value */
450 if (sign_posn
== 0 && is_negative
)
452 else if (sign_posn
== 1)
453 left_bytes
+= strlen (sign_string
);
454 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
455 left_bytes
+= strlen (sign_string
);
457 if (other_sign_posn
== 0 && !is_negative
)
459 else if (other_sign_posn
== 1)
460 other_left_bytes
+= strlen (other_sign_string
);
461 else if (other_cs_precedes
&&
462 (other_sign_posn
== 3 || other_sign_posn
== 4))
463 other_left_bytes
+= strlen (other_sign_string
);
465 /* Compare the number of bytes preceding the value for
466 each format, and set the padding accordingly */
467 if (other_left_bytes
> left_bytes
)
468 left_pad
= other_left_bytes
- left_bytes
;
473 /* Perhaps we'll someday make these things configurable so
474 better start using symbolic names now. */
475 #define left_paren '('
476 #define right_paren ')'
478 startp
= dest
; /* Remember start so we can compute length. */
480 while (left_pad
-- > 0)
483 if (sign_posn
== 0 && is_negative
)
484 out_char (left_paren
);
488 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
491 out_string (sign_string
);
492 if (sep_by_space
== 2)
496 if (print_curr_symbol
)
497 out_string (currency_symbol
);
501 if (print_curr_symbol
&& sep_by_space
== 2)
502 out_char (space_char
);
503 out_string (sign_string
);
504 if (sep_by_space
== 1)
505 /* POSIX.2 and SUS are not clear on this case, but C99
506 says a space follows the adjacent-symbol-and-sign */
510 if (print_curr_symbol
&& sep_by_space
== 1)
511 out_char (space_char
);
514 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
515 && sign_posn
!= 4 && sign_posn
!= 5)
516 out_string (sign_string
);
518 /* Print the number. */
520 f
._sbf
._f
._lock
= &lock
;
522 INTUSE(_IO_init
) ((_IO_FILE
*) &f
, 0);
523 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
524 INTUSE(_IO_str_init_static
) ((_IO_strfile
*) &f
, dest
,
525 (s
+ maxsize
) - dest
, dest
);
526 /* We clear the last available byte so we can find out whether
527 the numeric representation is too long. */
528 s
[maxsize
- 1] = '\0';
530 memset (&info
, '\0', sizeof (info
));
531 info
.prec
= right_prec
;
532 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
534 info
.is_long_double
= is_long_double
;
537 info
.extra
= 1; /* This means use values from LC_MONETARY. */
540 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
544 if (s
[maxsize
- 1] != '\0')
556 if (sep_by_space
== 1)
558 out_string (sign_string
);
561 if (print_curr_symbol
)
563 if ((sign_posn
== 3 && sep_by_space
== 2)
564 || (sign_posn
== 4 && sep_by_space
== 1)
565 || (sign_posn
== 2 && sep_by_space
== 1)
566 || (sign_posn
== 1 && sep_by_space
== 1)
567 || (sign_posn
== 0 && sep_by_space
== 1))
568 out_char (space_char
);
569 out_nstring (currency_symbol
, currency_symbol_len
);
574 if (sep_by_space
== 2)
576 out_string (sign_string
);
582 if (sep_by_space
== 2)
584 out_string (sign_string
);
587 if (sign_posn
== 0 && is_negative
)
588 out_char (right_paren
);
590 /* Now test whether the output width is filled. */
591 if (dest
- startp
< width
)
594 /* We simply have to fill using spaces. */
597 while (dest
- startp
< width
);
600 long int dist
= width
- (dest
- startp
);
601 for (char *cp
= dest
- 1; cp
>= startp
; --cp
)
607 startp
[--dist
] = ' ';
613 /* Terminate the string. */
620 ___strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
624 va_start (ap
, format
);
626 ssize_t res
= __vstrfmon_l (s
, maxsize
, loc
, format
, ap
);
632 ldbl_strong_alias (___strfmon_l
, __strfmon_l
)
633 ldbl_weak_alias (___strfmon_l
, strfmon_l
)