1 /* Formatting a monetary value according to the given locale.
2 Copyright (C) 1996, 1997, 2002, 2004, 2006 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
))
225 width
+= to_digit (*fmt
);
228 /* If we don't have enough room for the demanded width we
229 can stop now and return an error. */
230 if (dest
+ width
>= s
+ maxsize
)
237 /* Recognize left precision. */
240 if (!isdigit (*++fmt
))
242 __set_errno (EINVAL
);
245 left_prec
= to_digit (*fmt
);
247 while (isdigit (*++fmt
))
250 left_prec
+= to_digit (*fmt
);
254 /* Recognize right precision. */
257 if (!isdigit (*++fmt
))
259 __set_errno (EINVAL
);
262 right_prec
= to_digit (*fmt
);
264 while (isdigit (*++fmt
))
267 right_prec
+= to_digit (*fmt
);
271 /* Handle modifier. This is an extension. */
279 /* Handle format specifier. */
283 case 'i': { /* Use international currency symbol. */
284 const char *int_curr_symbol
;
286 int_curr_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
287 strncpy(int_symbol
, int_curr_symbol
, 3);
288 int_symbol
[3] = '\0';
290 currency_symbol_len
= 3;
291 currency_symbol
= &int_symbol
[0];
292 space_char
= int_curr_symbol
[3];
296 case 'n': /* Use national currency symbol. */
297 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
298 currency_symbol_len
= strlen (currency_symbol
);
302 default: /* Any unrecognized format is an error. */
303 __set_errno (EINVAL
);
307 /* If not specified by the format string now find the values for
308 the format specification. */
309 if (p_sign_posn
== -1)
310 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SIGN_POSN
: P_SIGN_POSN
);
311 if (n_sign_posn
== -1)
312 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SIGN_POSN
: N_SIGN_POSN
);
314 if (right_prec
== -1)
316 right_prec
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_FRAC_DIGITS
: FRAC_DIGITS
);
318 if (right_prec
== CHAR_MAX
)
322 /* If we have to print the digits grouped determine how many
323 extra characters this means. */
324 if (group
&& left_prec
!= -1)
325 left_prec
+= __guess_grouping (left_prec
,
326 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
327 *_NL_CURRENT (LC_MONETARY
,
330 /* Now it's time to get the value. */
331 if (is_long_double
== 1)
333 fpnum
.ldbl
= va_arg (ap
, long double);
334 is_negative
= fpnum
.ldbl
< 0;
336 fpnum
.ldbl
= -fpnum
.ldbl
;
340 fpnum
.dbl
= va_arg (ap
, double);
341 is_negative
= fpnum
.dbl
< 0;
343 fpnum
.dbl
= -fpnum
.dbl
;
346 /* We now know the sign of the value and can determine the format. */
349 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
350 /* If the locale does not specify a character for the
351 negative sign we use a '-'. */
352 if (*sign_string
== '\0')
353 sign_string
= (const char *) "-";
354 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
355 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
356 sign_posn
= n_sign_posn
;
358 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
359 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
360 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
361 other_sign_posn
= p_sign_posn
;
365 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
366 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_CS_PRECEDES
: P_CS_PRECEDES
);
367 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_P_SEP_BY_SPACE
: P_SEP_BY_SPACE
);
368 sign_posn
= p_sign_posn
;
370 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
371 if (*other_sign_string
== '\0')
372 other_sign_string
= (const char *) "-";
373 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_CS_PRECEDES
: N_CS_PRECEDES
);
374 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, int_format
? INT_N_SEP_BY_SPACE
: N_SEP_BY_SPACE
);
375 other_sign_posn
= n_sign_posn
;
378 /* Set default values for unspecified information. */
379 if (cs_precedes
!= 0)
381 if (other_cs_precedes
!= 0)
382 other_cs_precedes
= 1;
383 if (sep_by_space
== CHAR_MAX
)
385 if (other_sep_by_space
== CHAR_MAX
)
386 other_sep_by_space
= 0;
387 if (sign_posn
== CHAR_MAX
)
389 if (other_sign_posn
== CHAR_MAX
)
392 /* Check for degenerate cases */
393 if (sep_by_space
== 2)
395 if (sign_posn
== 0 ||
396 (sign_posn
== 1 && !cs_precedes
) ||
397 (sign_posn
== 2 && cs_precedes
))
398 /* sign and symbol are not adjacent, so no separator */
401 if (other_sep_by_space
== 2)
403 if (other_sign_posn
== 0 ||
404 (other_sign_posn
== 1 && !other_cs_precedes
) ||
405 (other_sign_posn
== 2 && other_cs_precedes
))
406 /* sign and symbol are not adjacent, so no separator */
407 other_sep_by_space
= 0;
410 /* Set the left precision and padding needed for alignment */
418 /* Set left_pad to number of spaces needed to align positive
419 and negative formats */
422 int other_left_bytes
= 0;
424 /* Work out number of bytes for currency string and separator
425 preceding the value */
428 left_bytes
+= currency_symbol_len
;
429 if (sep_by_space
!= 0)
433 if (other_cs_precedes
)
435 other_left_bytes
+= currency_symbol_len
;
436 if (other_sep_by_space
!= 0)
440 /* Work out number of bytes for the sign (or left parenthesis)
441 preceding the value */
442 if (sign_posn
== 0 && is_negative
)
444 else if (sign_posn
== 1)
445 left_bytes
+= strlen (sign_string
);
446 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
447 left_bytes
+= strlen (sign_string
);
449 if (other_sign_posn
== 0 && !is_negative
)
451 else if (other_sign_posn
== 1)
452 other_left_bytes
+= strlen (other_sign_string
);
453 else if (other_cs_precedes
&&
454 (other_sign_posn
== 3 || other_sign_posn
== 4))
455 other_left_bytes
+= strlen (other_sign_string
);
457 /* Compare the number of bytes preceding the value for
458 each format, and set the padding accordingly */
459 if (other_left_bytes
> left_bytes
)
460 left_pad
= other_left_bytes
- left_bytes
;
465 /* Perhaps we'll someday make these things configurable so
466 better start using symbolic names now. */
467 #define left_paren '('
468 #define right_paren ')'
470 startp
= dest
; /* Remember start so we can compute length. */
472 while (left_pad
-- > 0)
475 if (sign_posn
== 0 && is_negative
)
476 out_char (left_paren
);
480 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
483 out_string (sign_string
);
484 if (sep_by_space
== 2)
488 if (print_curr_symbol
)
489 out_string (currency_symbol
);
493 if (print_curr_symbol
&& sep_by_space
== 2)
494 out_char (space_char
);
495 out_string (sign_string
);
496 if (sep_by_space
== 1)
497 /* POSIX.2 and SUS are not clear on this case, but C99
498 says a space follows the adjacent-symbol-and-sign */
502 if (print_curr_symbol
&& sep_by_space
== 1)
503 out_char (space_char
);
506 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
507 && sign_posn
!= 4 && sign_posn
!= 5)
508 out_string (sign_string
);
510 /* Print the number. */
512 f
._sbf
._f
._lock
= &lock
;
514 INTUSE(_IO_init
) ((_IO_FILE
*) &f
, 0);
515 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
516 INTUSE(_IO_str_init_static
) ((_IO_strfile
*) &f
, dest
,
517 (s
+ maxsize
) - dest
, dest
);
518 /* We clear the last available byte so we can find out whether
519 the numeric representation is too long. */
520 s
[maxsize
- 1] = '\0';
522 memset (&info
, '\0', sizeof (info
));
523 info
.prec
= right_prec
;
524 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
526 info
.is_long_double
= is_long_double
;
529 info
.extra
= 1; /* This means use values from LC_MONETARY. */
532 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
536 if (s
[maxsize
- 1] != '\0')
548 if (sep_by_space
== 1)
550 out_string (sign_string
);
553 if (print_curr_symbol
)
555 if ((sign_posn
== 3 && sep_by_space
== 2)
556 || (sign_posn
== 4 && sep_by_space
== 1)
557 || (sign_posn
== 2 && sep_by_space
== 1)
558 || (sign_posn
== 1 && sep_by_space
== 1)
559 || (sign_posn
== 0 && sep_by_space
== 1))
560 out_char (space_char
);
561 out_nstring (currency_symbol
, currency_symbol_len
);
566 if (sep_by_space
== 2)
568 out_string (sign_string
);
574 if (sep_by_space
== 2)
576 out_string (sign_string
);
579 if (sign_posn
== 0 && is_negative
)
580 out_char (right_paren
);
582 /* Now test whether the output width is filled. */
583 if (dest
- startp
< width
)
586 /* We simply have to fill using spaces. */
589 while (dest
- startp
< width
);
592 int dist
= width
- (dest
- startp
);
594 for (cp
= dest
- 1; cp
>= startp
; --cp
)
600 startp
[--dist
] = ' ';
606 /* Terminate the string. */
613 ___strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
617 va_start (ap
, format
);
619 ssize_t res
= __vstrfmon_l (s
, maxsize
, loc
, format
, ap
);
625 ldbl_strong_alias (___strfmon_l
, __strfmon_l
)
626 ldbl_weak_alias (___strfmon_l
, strfmon_l
)