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 Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the 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 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, write to the Free
19 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
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
];
104 # ifdef _IO_MTSAFE_IO
110 struct printf_info info
;
111 va_list ap
; /* Scan through the varargs. */
112 char *dest
; /* Pointer so copy the output. */
113 const char *fmt
; /* Pointer that walks through format. */
115 va_start (ap
, format
);
120 /* Loop through the format-string. */
123 /* The floating-point value to output. */
127 __long_double_t ldbl
;
130 int print_curr_symbol
;
144 int other_sep_by_space
;
146 int other_cs_precedes
;
147 const char *sign_string
;
148 const char *other_sign_string
;
150 const char *currency_symbol
;
151 size_t currency_symbol_len
;
157 /* Process all character which do not introduce a format
165 /* "%%" means a single '%' character. */
173 /* Defaults for formatting. */
174 print_curr_symbol
= 1; /* Print the currency symbol. */
175 left_prec
= -1; /* No left precision specified. */
176 right_prec
= -1; /* No right precision specified. */
177 group
= 1; /* Print digits grouped. */
178 pad
= ' '; /* Fill character is <SP>. */
179 is_long_double
= 0; /* Double argument by default. */
180 p_sign_posn
= -1; /* This indicates whether the */
181 n_sign_posn
= -1; /* '(' flag is given. */
182 width
= -1; /* No width specified so far. */
183 left
= 0; /* Right justified by default. */
185 /* Parse group characters. */
190 case '=': /* Set fill character. */
195 __set_errno (EINVAL
);
200 case '^': /* Don't group digits. */
203 case '+': /* Use +/- for sign of number. */
204 if (n_sign_posn
!= -1)
206 __set_errno (EINVAL
);
210 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
211 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
213 case '(': /* Use ( ) for negative sign. */
214 if (n_sign_posn
!= -1)
216 __set_errno (EINVAL
);
223 case '!': /* Don't print the currency symbol. */
224 print_curr_symbol
= 0;
226 case '-': /* Print left justified. */
230 /* Will stop the loop. */;
235 /* If not specified by the format string now find the values for
236 the format specification. */
237 if (p_sign_posn
== -1)
238 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
239 if (n_sign_posn
== -1)
240 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
244 /* Parse field width. */
245 width
= to_digit (*fmt
);
247 while (isdigit (*++fmt
))
250 width
+= to_digit (*fmt
);
253 /* If we don't have enough room for the demanded width we
254 can stop now and return an error. */
255 if (dest
+ width
>= s
+ maxsize
)
263 /* Recognize left precision. */
266 if (!isdigit (*++fmt
))
268 __set_errno (EINVAL
);
272 left_prec
= to_digit (*fmt
);
274 while (isdigit (*++fmt
))
277 left_prec
+= to_digit (*fmt
);
281 /* Recognize right precision. */
284 if (!isdigit (*++fmt
))
286 __set_errno (EINVAL
);
290 right_prec
= to_digit (*fmt
);
292 while (isdigit (*++fmt
))
295 right_prec
+= to_digit (*fmt
);
299 /* Handle modifier. This is an extension. */
306 /* Handle format specifier. */
309 case 'i': /* Use international currency symbol. */
310 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
311 currency_symbol_len
= 3;
312 space_char
= currency_symbol
[3];
313 if (right_prec
== -1)
315 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == CHAR_MAX
)
318 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
321 case 'n': /* Use national currency symbol. */
322 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
323 currency_symbol_len
= strlen (currency_symbol
);
325 if (right_prec
== -1)
327 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == CHAR_MAX
)
330 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
333 default: /* Any unrecognized format is an error. */
334 __set_errno (EINVAL
);
339 /* If we have to print the digits grouped determine how many
340 extra characters this means. */
341 if (group
&& left_prec
!= -1)
342 left_prec
+= __guess_grouping (left_prec
,
343 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
344 *_NL_CURRENT (LC_MONETARY
,
347 /* Now it's time to get the value. */
348 if (is_long_double
== 1)
350 fpnum
.ldbl
= va_arg (ap
, long double);
351 is_negative
= fpnum
.ldbl
< 0;
353 fpnum
.ldbl
= -fpnum
.ldbl
;
357 fpnum
.dbl
= va_arg (ap
, double);
358 is_negative
= fpnum
.dbl
< 0;
360 fpnum
.dbl
= -fpnum
.dbl
;
363 /* We now know the sign of the value and can determine the format. */
366 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
367 /* If the locale does not specify a character for the
368 negative sign we use a '-'. */
369 if (*sign_string
== '\0')
370 sign_string
= (const char *) "-";
371 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
372 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
373 sign_posn
= n_sign_posn
;
375 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
376 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
377 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
378 other_sign_posn
= p_sign_posn
;
382 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
383 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
384 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
385 sign_posn
= p_sign_posn
;
387 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
388 if (*other_sign_string
== '\0')
389 other_sign_string
= (const char *) "-";
390 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
391 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
392 other_sign_posn
= n_sign_posn
;
395 /* Set default values for unspecified information. */
396 if (cs_precedes
!= 0)
398 if (other_cs_precedes
!= 0)
399 other_cs_precedes
= 1;
400 if (sep_by_space
== CHAR_MAX
)
402 if (other_sep_by_space
== CHAR_MAX
)
403 other_sep_by_space
= 0;
404 if (sign_posn
== CHAR_MAX
)
406 if (other_sign_posn
== CHAR_MAX
)
409 /* Check for degenerate cases */
410 if (sep_by_space
== 2)
412 if (sign_posn
== 0 ||
413 (sign_posn
== 1 && !cs_precedes
) ||
414 (sign_posn
== 2 && cs_precedes
))
415 /* sign and symbol are not adjacent, so no separator */
418 if (other_sep_by_space
== 2)
420 if (other_sign_posn
== 0 ||
421 (other_sign_posn
== 1 && !other_cs_precedes
) ||
422 (other_sign_posn
== 2 && other_cs_precedes
))
423 /* sign and symbol are not adjacent, so no separator */
424 other_sep_by_space
= 0;
427 /* Set the left precision and padding needed for alignment */
435 /* Set left_pad to number of spaces needed to align positive
436 and negative formats */
439 int other_left_bytes
= 0;
441 /* Work out number of bytes for currency string and separator
442 preceding the value */
445 left_bytes
+= currency_symbol_len
;
446 if (sep_by_space
!= 0)
450 if (other_cs_precedes
)
452 other_left_bytes
+= currency_symbol_len
;
453 if (other_sep_by_space
!= 0)
457 /* Work out number of bytes for the sign (or left parenthesis)
458 preceding the value */
459 if (sign_posn
== 0 && is_negative
)
461 else if (sign_posn
== 1)
462 left_bytes
+= strlen (sign_string
);
463 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
464 left_bytes
+= strlen (sign_string
);
466 if (other_sign_posn
== 0 && !is_negative
)
468 else if (other_sign_posn
== 1)
469 other_left_bytes
+= strlen (other_sign_string
);
470 else if (other_cs_precedes
&&
471 (other_sign_posn
== 3 || other_sign_posn
== 4))
472 other_left_bytes
+= strlen (other_sign_string
);
474 /* Compare the number of bytes preceding the value for
475 each format, and set the padding accordingly */
476 if (other_left_bytes
> left_bytes
)
477 left_pad
= other_left_bytes
- left_bytes
;
482 /* Perhaps we'll someday make these things configurable so
483 better start using symbolic names now. */
484 #define left_paren '('
485 #define right_paren ')'
487 startp
= dest
; /* Remember start so we can compute length. */
489 while (left_pad
-- > 0)
492 if (sign_posn
== 0 && is_negative
)
493 out_char (left_paren
);
497 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
500 out_string (sign_string
);
501 if (sep_by_space
== 2)
505 if (print_curr_symbol
)
507 out_string (currency_symbol
);
511 if (sep_by_space
== 2)
512 out_char (space_char
);
513 out_string (sign_string
);
514 if (sep_by_space
== 1)
515 /* POSIX.2 and SUS are not clear on this case, but C99
516 says a space follows the adjacent-symbol-and-sign */
520 if (sep_by_space
== 1)
521 out_char (space_char
);
525 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
526 && sign_posn
!= 4 && sign_posn
!= 5)
527 out_string (sign_string
);
529 /* Print the number. */
531 # ifdef _IO_MTSAFE_IO
532 f
._sbf
._f
._lock
= &lock
;
534 _IO_init ((_IO_FILE
*) &f
, 0);
535 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
536 _IO_str_init_static ((_IO_strfile
*) &f
, dest
, (s
+ maxsize
) - dest
, dest
);
538 memset ((void *) &f
, 0, sizeof (f
));
539 f
.__magic
= _IOMAGIC
;
540 f
.__mode
.__write
= 1;
541 /* The buffer size is one less than MAXLEN
542 so we have space for the null terminator. */
543 f
.__bufp
= f
.__buffer
= (char *) dest
;
544 f
.__bufsize
= (s
+ maxsize
) - dest
;
545 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
546 f
.__get_limit
= f
.__buffer
;
547 /* After the buffer is full (MAXLEN characters have been written),
548 any more characters written will go to the bit bucket. */
549 f
.__room_funcs
= __default_room_functions
;
550 f
.__io_funcs
.__write
= NULL
;
553 /* We clear the last available byte so we can find out whether
554 the numeric representation is too long. */
555 s
[maxsize
- 1] = '\0';
557 info
.prec
= right_prec
;
558 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
560 info
.is_long_double
= is_long_double
;
569 info
.extra
= 1; /* This means use values from LC_MONETARY. */
573 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
580 if (s
[maxsize
- 1] != '\0')
592 if (sep_by_space
== 1)
594 out_string (sign_string
);
597 if (print_curr_symbol
)
599 if ((sign_posn
== 3 && sep_by_space
== 2)
600 || (sign_posn
== 4 && sep_by_space
== 1)
601 || (sign_posn
== 2 && sep_by_space
== 1)
602 || (sign_posn
== 1 && sep_by_space
== 1)
603 || (sign_posn
== 0 && sep_by_space
== 1))
604 out_char (space_char
);
605 out_nstring (currency_symbol
, currency_symbol_len
);
608 if (sep_by_space
== 2)
610 out_string (sign_string
);
617 if (sep_by_space
== 2)
619 out_string (sign_string
);
622 if (sign_posn
== 0 && is_negative
)
623 out_char (right_paren
);
625 /* Now test whether the output width is filled. */
626 if (dest
- startp
< width
)
629 /* We simply have to fill using spaces. */
632 while (dest
- startp
< width
);
635 int dist
= width
- (dest
- startp
);
637 for (cp
= dest
- 1; cp
>= startp
; --cp
)
643 startp
[--dist
] = ' ';
649 /* Terminate the string. */