1 /* Formatting a monetary value according to the given locale.
2 Copyright (C) 1996, 1997, 2002, 2004 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
27 # include "../libio/libioP.h"
28 # include "../libio/strfile.h"
34 #include "../locale/localeinfo.h"
37 #define out_char(Ch) \
39 if (dest >= s + maxsize - 1) \
41 __set_errno (E2BIG); \
48 #define out_string(String) \
50 const char *_s = (String); \
55 #define out_nstring(String, N) \
58 const char *_s = (String); \
63 #define to_digit(Ch) ((Ch) - '0')
66 /* We use this code also for the extended locale handling where the
67 function gets as an additional argument the locale which has to be
68 used. To access the values we have to redefine the _NL_CURRENT
71 #define _NL_CURRENT(category, item) \
72 (current->values[_NL_ITEM_INDEX (item)].string)
74 extern int __printf_fp (FILE *, const struct printf_info
*,
76 libc_hidden_proto (__printf_fp
)
77 /* This function determines the number of digit groups in the output.
78 The definition is in printf_fp.c. */
79 extern unsigned int __guess_grouping (unsigned int intdig_max
,
80 const char *grouping
, wchar_t sepchar
);
83 /* We have to overcome some problems with this implementation. On the
84 one hand the strfmon() function is specified in XPG4 and of course
85 it has to follow this. But on the other hand POSIX.2 specifies
86 some information in the LC_MONETARY category which should be used,
87 too. Some of the information contradicts the information which can
88 be specified in format string. */
90 __vstrfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
,
93 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
102 struct printf_info info
;
103 char *dest
; /* Pointer so copy the output. */
104 const char *fmt
; /* Pointer that walks through format. */
109 /* Loop through the format-string. */
112 /* The floating-point value to output. */
116 __long_double_t ldbl
;
120 int print_curr_symbol
;
134 int other_sep_by_space
;
136 int other_cs_precedes
;
137 const char *sign_string
;
138 const char *other_sign_string
;
140 const char *currency_symbol
;
141 size_t currency_symbol_len
;
147 /* Process all character which do not introduce a format
155 /* "%%" means a single '%' character. */
163 /* Defaults for formatting. */
164 int_format
= 0; /* Use international curr. symbol */
165 print_curr_symbol
= 1; /* Print the currency symbol. */
166 left_prec
= -1; /* No left precision specified. */
167 right_prec
= -1; /* No right precision specified. */
168 group
= 1; /* Print digits grouped. */
169 pad
= ' '; /* Fill character is <SP>. */
170 is_long_double
= 0; /* Double argument by default. */
171 p_sign_posn
= -1; /* This indicates whether the */
172 n_sign_posn
= -1; /* '(' flag is given. */
173 width
= -1; /* No width specified so far. */
174 left
= 0; /* Right justified by default. */
176 /* Parse group characters. */
181 case '=': /* Set fill character. */
186 __set_errno (EINVAL
);
190 case '^': /* Don't group digits. */
193 case '+': /* Use +/- for sign of number. */
194 if (n_sign_posn
!= -1)
196 __set_errno (EINVAL
);
199 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
200 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
202 case '(': /* Use ( ) for negative sign. */
203 if (n_sign_posn
!= -1)
205 __set_errno (EINVAL
);
211 case '!': /* Don't print the currency symbol. */
212 print_curr_symbol
= 0;
214 case '-': /* Print left justified. */
218 /* Will stop the loop. */;
225 /* Parse field width. */
226 width
= to_digit (*fmt
);
228 while (isdigit (*++fmt
))
231 width
+= to_digit (*fmt
);
234 /* If we don't have enough room for the demanded width we
235 can stop now and return an error. */
236 if (dest
+ width
>= s
+ maxsize
)
243 /* Recognize left precision. */
246 if (!isdigit (*++fmt
))
248 __set_errno (EINVAL
);
251 left_prec
= to_digit (*fmt
);
253 while (isdigit (*++fmt
))
256 left_prec
+= to_digit (*fmt
);
260 /* Recognize right precision. */
263 if (!isdigit (*++fmt
))
265 __set_errno (EINVAL
);
268 right_prec
= to_digit (*fmt
);
270 while (isdigit (*++fmt
))
273 right_prec
+= to_digit (*fmt
);
277 /* 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
)
495 out_string (currency_symbol
);
499 if (sep_by_space
== 2)
500 out_char (space_char
);
501 out_string (sign_string
);
502 if (sep_by_space
== 1)
503 /* POSIX.2 and SUS are not clear on this case, but C99
504 says a space follows the adjacent-symbol-and-sign */
508 if (sep_by_space
== 1)
509 out_char (space_char
);
513 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
514 && sign_posn
!= 4 && sign_posn
!= 5)
515 out_string (sign_string
);
517 /* Print the number. */
519 # ifdef _IO_MTSAFE_IO
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
);
527 memset ((void *) &f
, 0, sizeof (f
));
528 f
.__magic
= _IOMAGIC
;
529 f
.__mode
.__write
= 1;
530 /* The buffer size is one less than MAXLEN
531 so we have space for the null terminator. */
532 f
.__bufp
= f
.__buffer
= (char *) dest
;
533 f
.__bufsize
= (s
+ maxsize
) - dest
;
534 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
535 f
.__get_limit
= f
.__buffer
;
536 /* After the buffer is full (MAXLEN characters have been written),
537 any more characters written will go to the bit bucket. */
538 f
.__room_funcs
= __default_room_functions
;
539 f
.__io_funcs
.__write
= NULL
;
542 /* We clear the last available byte so we can find out whether
543 the numeric representation is too long. */
544 s
[maxsize
- 1] = '\0';
546 memset (&info
, '\0', sizeof (info
));
547 info
.prec
= right_prec
;
548 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
550 info
.is_long_double
= is_long_double
;
553 info
.extra
= 1; /* This means use values from LC_MONETARY. */
556 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
560 if (s
[maxsize
- 1] != '\0')
572 if (sep_by_space
== 1)
574 out_string (sign_string
);
577 if (print_curr_symbol
)
579 if ((sign_posn
== 3 && sep_by_space
== 2)
580 || (sign_posn
== 4 && sep_by_space
== 1)
581 || (sign_posn
== 2 && sep_by_space
== 1)
582 || (sign_posn
== 1 && sep_by_space
== 1)
583 || (sign_posn
== 0 && sep_by_space
== 1))
584 out_char (space_char
);
585 out_nstring (currency_symbol
, currency_symbol_len
);
588 if (sep_by_space
== 2)
590 out_string (sign_string
);
597 if (sep_by_space
== 2)
599 out_string (sign_string
);
602 if (sign_posn
== 0 && is_negative
)
603 out_char (right_paren
);
605 /* Now test whether the output width is filled. */
606 if (dest
- startp
< width
)
609 /* We simply have to fill using spaces. */
612 while (dest
- startp
< width
);
615 int dist
= width
- (dest
- startp
);
617 for (cp
= dest
- 1; cp
>= startp
; --cp
)
623 startp
[--dist
] = ' ';
629 /* Terminate the string. */
636 __strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
640 va_start (ap
, format
);
642 ssize_t res
= __vstrfmon_l (s
, maxsize
, loc
, format
, ap
);
648 weak_alias (__strfmon_l
, strfmon_l
)