1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996,1997,1998,1999,2000,2001 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>
5 and Jochen Hein <Jochen.Hein@informatik.TU-Clausthal.de>, 1996.
7 The GNU C Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with the GNU C Library; see the file COPYING.LIB. If not,
19 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
28 # include "../libio/libioP.h"
29 # include "../libio/strfile.h"
35 #include "../locale/localeinfo.h"
38 #define out_char(Ch) \
40 if (dest >= s + maxsize - 1) \
42 __set_errno (E2BIG); \
49 #define out_string(String) \
51 const char *_s = (String); \
56 #define out_nstring(String, N) \
59 const char *_s = (String); \
64 #define to_digit(Ch) ((Ch) - '0')
67 /* We use this code also for the extended locale handling where the
68 function gets as an additional argument the locale which has to be
69 used. To access the values we have to redefine the _NL_CURRENT
71 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
73 # define _NL_CURRENT(category, item) \
74 (current->values[_NL_ITEM_INDEX (item)].string)
77 extern int __printf_fp (FILE *, const struct printf_info
*,
79 /* This function determines the number of digit groups in the output.
80 The definition is in printf_fp.c. */
81 extern unsigned int __guess_grouping (unsigned int intdig_max
,
82 const char *grouping
, wchar_t sepchar
);
85 /* We have to overcome some problems with this implementation. On the
86 one hand the strfmon() function is specified in XPG4 and of course
87 it has to follow this. But on the other hand POSIX.2 specifies
88 some information in the LC_MONETARY category which should be used,
89 too. Some of the information contradicts the information which can
90 be specified in format string. */
91 #ifndef USE_IN_EXTENDED_LOCALE_MODEL
93 strfmon (char *s
, size_t maxsize
, const char *format
, ...)
96 __strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
99 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
100 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
107 struct printf_info info
;
108 va_list ap
; /* Scan through the varargs. */
109 char *dest
; /* Pointer so copy the output. */
110 const char *fmt
; /* Pointer that walks through format. */
112 va_start (ap
, format
);
117 /* Loop through the format-string. */
120 /* The floating-point value to output. */
124 __long_double_t ldbl
;
127 int print_curr_symbol
;
141 int other_sep_by_space
;
143 int other_cs_precedes
;
144 const char *sign_string
;
145 const char *other_sign_string
;
147 const char *currency_symbol
;
148 size_t currency_symbol_len
;
154 /* Process all character which do not introduce a format
162 /* "%%" means a single '%' character. */
170 /* Defaults for formatting. */
171 print_curr_symbol
= 1; /* Print the currency symbol. */
172 left_prec
= -1; /* No left precision specified. */
173 right_prec
= -1; /* No right precision specified. */
174 group
= 1; /* Print digits grouped. */
175 pad
= ' '; /* Fill character is <SP>. */
176 is_long_double
= 0; /* Double argument by default. */
177 p_sign_posn
= -1; /* This indicates whether the */
178 n_sign_posn
= -1; /* '(' flag is given. */
179 width
= -1; /* No width specified so far. */
180 left
= 0; /* Right justified by default. */
182 /* Parse group characters. */
187 case '=': /* Set fill character. */
192 __set_errno (EINVAL
);
197 case '^': /* Don't group digits. */
200 case '+': /* Use +/- for sign of number. */
201 if (n_sign_posn
!= -1)
203 __set_errno (EINVAL
);
207 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
208 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
210 case '(': /* Use ( ) for negative sign. */
211 if (n_sign_posn
!= -1)
213 __set_errno (EINVAL
);
220 case '!': /* Don't print the currency symbol. */
221 print_curr_symbol
= 0;
223 case '-': /* Print left justified. */
227 /* Will stop the loop. */;
232 /* If not specified by the format string now find the values for
233 the format specification. */
234 if (p_sign_posn
== -1)
235 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
236 if (n_sign_posn
== -1)
237 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
241 /* Parse field width. */
242 width
= to_digit (*fmt
);
244 while (isdigit (*++fmt
))
247 width
+= to_digit (*fmt
);
250 /* If we don't have enough room for the demanded width we
251 can stop now and return an error. */
252 if (dest
+ width
>= s
+ maxsize
)
260 /* Recognize left precision. */
263 if (!isdigit (*++fmt
))
265 __set_errno (EINVAL
);
269 left_prec
= to_digit (*fmt
);
271 while (isdigit (*++fmt
))
274 left_prec
+= to_digit (*fmt
);
278 /* Recognize right precision. */
281 if (!isdigit (*++fmt
))
283 __set_errno (EINVAL
);
287 right_prec
= to_digit (*fmt
);
289 while (isdigit (*++fmt
))
292 right_prec
+= to_digit (*fmt
);
296 /* Handle modifier. This is an extension. */
303 /* Handle format specifier. */
306 case 'i': /* Use international currency symbol. */
307 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
308 currency_symbol_len
= 3;
309 space_char
= currency_symbol
[3];
310 if (right_prec
== -1)
312 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == CHAR_MAX
)
315 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
318 case 'n': /* Use national currency symbol. */
319 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
320 currency_symbol_len
= strlen (currency_symbol
);
322 if (right_prec
== -1)
324 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == CHAR_MAX
)
327 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
330 default: /* Any unrecognized format is an error. */
331 __set_errno (EINVAL
);
336 /* If we have to print the digits grouped determine how many
337 extra characters this means. */
338 if (group
&& left_prec
!= -1)
339 left_prec
+= __guess_grouping (left_prec
,
340 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
341 *_NL_CURRENT (LC_MONETARY
,
344 /* Now it's time to get the value. */
345 if (is_long_double
== 1)
347 fpnum
.ldbl
= va_arg (ap
, long double);
348 is_negative
= fpnum
.ldbl
< 0;
350 fpnum
.ldbl
= -fpnum
.ldbl
;
354 fpnum
.dbl
= va_arg (ap
, double);
355 is_negative
= fpnum
.dbl
< 0;
357 fpnum
.dbl
= -fpnum
.dbl
;
360 /* We now know the sign of the value and can determine the format. */
363 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
364 /* If the locale does not specify a character for the
365 negative sign we use a '-'. */
366 if (*sign_string
== '\0')
367 sign_string
= (const char *) "-";
368 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
369 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
370 sign_posn
= n_sign_posn
;
372 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
373 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
374 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
375 other_sign_posn
= p_sign_posn
;
379 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
380 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
381 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
382 sign_posn
= p_sign_posn
;
384 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
385 if (*other_sign_string
== '\0')
386 other_sign_string
= (const char *) "-";
387 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
388 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
389 other_sign_posn
= n_sign_posn
;
392 /* Set default values for unspecified information. */
393 if (cs_precedes
!= 0)
395 if (other_cs_precedes
!= 0)
396 other_cs_precedes
= 1;
397 if (sep_by_space
== CHAR_MAX
)
399 if (other_sep_by_space
== CHAR_MAX
)
400 other_sep_by_space
= 0;
401 if (sign_posn
== CHAR_MAX
)
403 if (other_sign_posn
== CHAR_MAX
)
406 /* Check for degenerate cases */
407 if (sep_by_space
== 2)
409 if (sign_posn
== 0 ||
410 (sign_posn
== 1 && !cs_precedes
) ||
411 (sign_posn
== 2 && cs_precedes
))
412 /* sign and symbol are not adjacent, so no separator */
415 if (other_sep_by_space
== 2)
417 if (other_sign_posn
== 0 ||
418 (other_sign_posn
== 1 && !other_cs_precedes
) ||
419 (other_sign_posn
== 2 && other_cs_precedes
))
420 /* sign and symbol are not adjacent, so no separator */
421 other_sep_by_space
= 0;
424 /* Set the left precision and padding needed for alignment */
432 /* Set left_pad to number of spaces needed to align positive
433 and negative formats */
436 int other_left_bytes
= 0;
438 /* Work out number of bytes for currency string and separator
439 preceding the value */
442 left_bytes
+= currency_symbol_len
;
443 if (sep_by_space
!= 0)
447 if (other_cs_precedes
)
449 other_left_bytes
+= currency_symbol_len
;
450 if (other_sep_by_space
!= 0)
454 /* Work out number of bytes for the sign (or left parenthesis)
455 preceding the value */
456 if (sign_posn
== 0 && is_negative
)
458 else if (sign_posn
== 1)
459 left_bytes
+= strlen (sign_string
);
460 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
461 left_bytes
+= strlen (sign_string
);
463 if (other_sign_posn
== 0 && !is_negative
)
465 else if (other_sign_posn
== 1)
466 other_left_bytes
+= strlen (other_sign_string
);
467 else if (other_cs_precedes
&&
468 (other_sign_posn
== 3 || other_sign_posn
== 4))
469 other_left_bytes
+= strlen (other_sign_string
);
471 /* Compare the number of bytes preceding the value for
472 each format, and set the padding accordingly */
473 if (other_left_bytes
> left_bytes
)
474 left_pad
= other_left_bytes
- left_bytes
;
479 /* Perhaps we'll someday make these things configurable so
480 better start using symbolic names now. */
481 #define left_paren '('
482 #define right_paren ')'
484 startp
= dest
; /* Remember start so we can compute length. */
486 while (left_pad
-- > 0)
489 if (sign_posn
== 0 && is_negative
)
490 out_char (left_paren
);
494 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
497 out_string (sign_string
);
498 if (sep_by_space
== 2)
502 if (print_curr_symbol
)
504 out_string (currency_symbol
);
508 if (sep_by_space
== 2)
509 out_char (space_char
);
510 out_string (sign_string
);
511 if (sep_by_space
== 1)
512 /* POSIX.2 and SUS are not clear on this case, but C99
513 says a space follows the adjacent-symbol-and-sign */
517 if (sep_by_space
== 1)
518 out_char (space_char
);
522 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
523 && sign_posn
!= 4 && sign_posn
!= 5)
524 out_string (sign_string
);
526 /* Print the number. */
528 _IO_init ((_IO_FILE
*) &f
, 0);
529 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
530 _IO_str_init_static ((_IO_strfile
*) &f
, dest
, (s
+ maxsize
) - dest
, dest
);
532 memset ((void *) &f
, 0, sizeof (f
));
533 f
.__magic
= _IOMAGIC
;
534 f
.__mode
.__write
= 1;
535 /* The buffer size is one less than MAXLEN
536 so we have space for the null terminator. */
537 f
.__bufp
= f
.__buffer
= (char *) dest
;
538 f
.__bufsize
= (s
+ maxsize
) - dest
;
539 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
540 f
.__get_limit
= f
.__buffer
;
541 /* After the buffer is full (MAXLEN characters have been written),
542 any more characters written will go to the bit bucket. */
543 f
.__room_funcs
= __default_room_functions
;
544 f
.__io_funcs
.__write
= NULL
;
547 /* We clear the last available byte so we can find out whether
548 the numeric representation is too long. */
549 s
[maxsize
- 1] = '\0';
551 info
.prec
= right_prec
;
552 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
554 info
.is_long_double
= is_long_double
;
563 info
.extra
= 1; /* This means use values from LC_MONETARY. */
567 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
574 if (s
[maxsize
- 1] != '\0')
586 if (sep_by_space
== 1)
588 out_string (sign_string
);
591 if (print_curr_symbol
)
593 if ((sign_posn
== 3 && sep_by_space
== 2)
594 || (sign_posn
== 4 && sep_by_space
== 1)
595 || (sign_posn
== 2 && sep_by_space
== 1)
596 || (sign_posn
== 1 && sep_by_space
== 1)
597 || (sign_posn
== 0 && sep_by_space
== 1))
598 out_char (space_char
);
599 out_nstring (currency_symbol
, currency_symbol_len
);
602 if (sep_by_space
== 2)
604 out_string (sign_string
);
611 if (sep_by_space
== 2)
613 out_string (sign_string
);
616 if (sign_posn
== 0 && is_negative
)
617 out_char (right_paren
);
619 /* Now test whether the output width is filled. */
620 if (dest
- startp
< width
)
623 /* We simply have to fill using spaces. */
626 while (dest
- startp
< width
);
629 int dist
= width
- (dest
- startp
);
631 for (cp
= dest
- 1; cp
>= startp
; --cp
)
637 startp
[--dist
] = ' ';
643 /* Terminate the string. */