1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996, 1997, 1998, 1999, 2000 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. */
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
70 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
72 # define _NL_CURRENT(category, item) \
73 (current->values[_NL_ITEM_INDEX (item)].string)
76 extern int __printf_fp (FILE *, const struct printf_info
*,
78 /* This function determines the number of digit groups in the output.
79 The definition is in printf_fp.c. */
80 extern unsigned int __guess_grouping (unsigned int intdig_max
,
81 const char *grouping
, wchar_t sepchar
);
84 /* We have to overcome some problems with this implementation. On the
85 one hand the strfmon() function is specified in XPG4 and of course
86 it has to follow this. But on the other hand POSIX.2 specifies
87 some information in the LC_MONETARY category which should be used,
88 too. Some of the information contradicts the information which can
89 be specified in format string. */
90 #ifndef USE_IN_EXTENDED_LOCALE_MODEL
92 strfmon (char *s
, size_t maxsize
, const char *format
, ...)
95 __strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
98 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
99 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
106 struct printf_info info
;
107 va_list ap
; /* Scan through the varargs. */
108 char *dest
; /* Pointer so copy the output. */
109 const char *fmt
; /* Pointer that walks through format. */
111 va_start (ap
, format
);
116 /* Loop through the format-string. */
119 /* The floating-point value to output. */
123 __long_double_t ldbl
;
126 int print_curr_symbol
;
140 int other_sep_by_space
;
142 int other_cs_precedes
;
143 const char *sign_string
;
144 const char *other_sign_string
;
146 const char *currency_symbol
;
147 size_t currency_symbol_len
;
153 /* Process all character which do not introduce a format
161 /* "%%" means a single '%' character. */
169 /* Defaults for formatting. */
170 print_curr_symbol
= 1; /* Print the currency symbol. */
171 left_prec
= -1; /* No left precision specified. */
172 right_prec
= -1; /* No right precision specified. */
173 group
= 1; /* Print digits grouped. */
174 pad
= ' '; /* Fill character is <SP>. */
175 is_long_double
= 0; /* Double argument by default. */
176 p_sign_posn
= -1; /* This indicates whether the */
177 n_sign_posn
= -1; /* '(' flag is given. */
178 width
= -1; /* No width specified so far. */
179 left
= 0; /* Right justified by default. */
181 /* Parse group characters. */
186 case '=': /* Set fill character. */
191 __set_errno (EINVAL
);
196 case '^': /* Don't group digits. */
199 case '+': /* Use +/- for sign of number. */
200 if (n_sign_posn
!= -1)
202 __set_errno (EINVAL
);
206 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
207 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
209 case '(': /* Use ( ) for negative sign. */
210 if (n_sign_posn
!= -1)
212 __set_errno (EINVAL
);
219 case '!': /* Don't print the currency symbol. */
220 print_curr_symbol
= 0;
222 case '-': /* Print left justified. */
226 /* Will stop the loop. */;
231 /* If not specified by the format string now find the values for
232 the format specification. */
233 if (p_sign_posn
== -1)
234 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
235 if (n_sign_posn
== -1)
236 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
240 /* Parse field width. */
241 width
= to_digit (*fmt
);
243 while (isdigit (*++fmt
))
246 width
+= to_digit (*fmt
);
249 /* If we don't have enough room for the demanded width we
250 can stop now and return an error. */
251 if (dest
+ width
>= s
+ maxsize
)
259 /* Recognize left precision. */
262 if (!isdigit (*++fmt
))
264 __set_errno (EINVAL
);
268 left_prec
= to_digit (*fmt
);
270 while (isdigit (*++fmt
))
273 left_prec
+= to_digit (*fmt
);
277 /* Recognize right precision. */
280 if (!isdigit (*++fmt
))
282 __set_errno (EINVAL
);
286 right_prec
= to_digit (*fmt
);
288 while (isdigit (*++fmt
))
291 right_prec
+= to_digit (*fmt
);
295 /* Handle modifier. This is an extension. */
302 /* Handle format specifier. */
305 case 'i': /* Use international currency symbol. */
306 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
307 currency_symbol_len
= 3;
308 space_char
= currency_symbol
[3];
309 if (right_prec
== -1)
311 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == CHAR_MAX
)
314 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
317 case 'n': /* Use national currency symbol. */
318 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
319 currency_symbol_len
= strlen (currency_symbol
);
321 if (right_prec
== -1)
323 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == CHAR_MAX
)
326 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
329 default: /* Any unrecognized format is an error. */
330 __set_errno (EINVAL
);
335 /* If we have to print the digits grouped determine how many
336 extra characters this means. */
337 if (group
&& left_prec
!= -1)
338 left_prec
+= __guess_grouping (left_prec
,
339 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
340 *_NL_CURRENT (LC_MONETARY
,
343 /* Now it's time to get the value. */
344 if (is_long_double
== 1)
346 fpnum
.ldbl
= va_arg (ap
, long double);
347 is_negative
= fpnum
.ldbl
< 0;
349 fpnum
.ldbl
= -fpnum
.ldbl
;
353 fpnum
.dbl
= va_arg (ap
, double);
354 is_negative
= fpnum
.dbl
< 0;
356 fpnum
.dbl
= -fpnum
.dbl
;
359 /* We now know the sign of the value and can determine the format. */
362 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
363 /* If the locale does not specify a character for the
364 negative sign we use a '-'. */
365 if (*sign_string
== '\0')
366 sign_string
= (const char *) "-";
367 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
368 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
369 sign_posn
= n_sign_posn
;
371 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
372 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
373 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
374 other_sign_posn
= p_sign_posn
;
378 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
379 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
380 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
381 sign_posn
= p_sign_posn
;
383 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
384 if (*other_sign_string
== '\0')
385 other_sign_string
= (const char *) "-";
386 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
387 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
388 other_sign_posn
= n_sign_posn
;
391 /* Set default values for unspecified information. */
392 if (cs_precedes
!= 0)
394 if (other_cs_precedes
!= 0)
395 other_cs_precedes
= 1;
396 if (sep_by_space
== CHAR_MAX
)
398 if (other_sep_by_space
== CHAR_MAX
)
399 other_sep_by_space
= 0;
400 if (sign_posn
== CHAR_MAX
)
402 if (other_sign_posn
== CHAR_MAX
)
405 /* Check for degenerate cases */
406 if (sep_by_space
== 2)
408 if (sign_posn
== 0 ||
409 (sign_posn
== 1 && !cs_precedes
) ||
410 (sign_posn
== 2 && cs_precedes
))
411 /* sign and symbol are not adjacent, so no separator */
414 if (other_sep_by_space
== 2)
416 if (other_sign_posn
== 0 ||
417 (other_sign_posn
== 1 && !other_cs_precedes
) ||
418 (other_sign_posn
== 2 && other_cs_precedes
))
419 /* sign and symbol are not adjacent, so no separator */
420 other_sep_by_space
= 0;
423 /* Set the left precision and padding needed for alignment */
431 /* Set left_pad to number of spaces needed to align positive
432 and negative formats */
435 int other_left_bytes
= 0;
437 /* Work out number of bytes for currency string and separator
438 preceding the value */
441 left_bytes
+= currency_symbol_len
;
442 if (sep_by_space
!= 0)
446 if (other_cs_precedes
)
448 other_left_bytes
+= currency_symbol_len
;
449 if (other_sep_by_space
!= 0)
453 /* Work out number of bytes for the sign (or left parenthesis)
454 preceding the value */
455 if (sign_posn
== 0 && is_negative
)
457 else if (sign_posn
== 1)
458 left_bytes
+= strlen (sign_string
);
459 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
460 left_bytes
+= strlen (sign_string
);
462 if (other_sign_posn
== 0 && !is_negative
)
464 else if (other_sign_posn
== 1)
465 other_left_bytes
+= strlen (other_sign_string
);
466 else if (other_cs_precedes
&&
467 (other_sign_posn
== 3 || other_sign_posn
== 4))
468 other_left_bytes
+= strlen (other_sign_string
);
470 /* Compare the number of bytes preceding the value for
471 each format, and set the padding accordingly */
472 if (other_left_bytes
> left_bytes
)
473 left_pad
= other_left_bytes
- left_bytes
;
478 /* Perhaps we'll someday make these things configurable so
479 better start using symbolic names now. */
480 #define left_paren '('
481 #define right_paren ')'
483 startp
= dest
; /* Remember start so we can compute length. */
485 while (left_pad
-- > 0)
488 if (sign_posn
== 0 && is_negative
)
489 out_char (left_paren
);
493 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
496 out_string (sign_string
);
497 if (sep_by_space
== 2)
501 if (print_curr_symbol
)
503 out_string (currency_symbol
);
507 if (sep_by_space
== 2)
508 out_char (space_char
);
509 out_string (sign_string
);
510 if (sep_by_space
== 1)
511 /* POSIX.2 and SUS are not clear on this case, but C99
512 says a space follows the adjacent-symbol-and-sign */
516 if (sep_by_space
== 1)
517 out_char (space_char
);
521 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
522 && sign_posn
!= 4 && sign_posn
!= 5)
523 out_string (sign_string
);
525 /* Print the number. */
527 _IO_init ((_IO_FILE
*) &f
, 0);
528 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
529 _IO_str_init_static ((_IO_strfile
*) &f
, dest
, (s
+ maxsize
) - dest
, dest
);
531 memset ((void *) &f
, 0, sizeof (f
));
532 f
.__magic
= _IOMAGIC
;
533 f
.__mode
.__write
= 1;
534 /* The buffer size is one less than MAXLEN
535 so we have space for the null terminator. */
536 f
.__bufp
= f
.__buffer
= (char *) dest
;
537 f
.__bufsize
= (s
+ maxsize
) - dest
;
538 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
539 f
.__get_limit
= f
.__buffer
;
540 /* After the buffer is full (MAXLEN characters have been written),
541 any more characters written will go to the bit bucket. */
542 f
.__room_funcs
= __default_room_functions
;
543 f
.__io_funcs
.__write
= NULL
;
546 /* We clear the last available byte so we can find out whether
547 the numeric representation is too long. */
548 s
[maxsize
- 1] = '\0';
550 info
.prec
= right_prec
;
551 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
553 info
.is_long_double
= is_long_double
;
562 info
.extra
= 1; /* This means use values from LC_MONETARY. */
566 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
573 if (s
[maxsize
- 1] != '\0')
585 if (sep_by_space
== 1)
587 out_string (sign_string
);
590 if (print_curr_symbol
)
592 if ((sign_posn
== 3 && sep_by_space
== 2)
593 || (sign_posn
== 4 && sep_by_space
== 1)
594 || (sign_posn
== 2 && sep_by_space
== 1)
595 || (sign_posn
== 1 && sep_by_space
== 1)
596 || (sign_posn
== 0 && sep_by_space
== 1))
597 out_char (space_char
);
598 out_nstring (currency_symbol
, currency_symbol_len
);
601 if (sep_by_space
== 2)
603 out_string (sign_string
);
610 if (sep_by_space
== 2)
612 out_string (sign_string
);
615 if (sign_posn
== 0 && is_negative
)
616 out_char (right_paren
);
618 /* Now test whether the output width is filled. */
619 if (dest
- startp
< width
)
622 /* We simply have to fill using spaces. */
625 while (dest
- startp
< width
);
628 int dist
= width
- (dest
- startp
);
630 for (cp
= dest
- 1; cp
>= startp
; --cp
)
636 startp
[--dist
] = ' ';
642 /* Terminate the string. */