1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996-2001, 2002 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 libc_hidden_proto (__printf_fp
)
80 /* This function determines the number of digit groups in the output.
81 The definition is in printf_fp.c. */
82 extern unsigned int __guess_grouping (unsigned int intdig_max
,
83 const char *grouping
, wchar_t sepchar
);
86 /* We have to overcome some problems with this implementation. On the
87 one hand the strfmon() function is specified in XPG4 and of course
88 it has to follow this. But on the other hand POSIX.2 specifies
89 some information in the LC_MONETARY category which should be used,
90 too. Some of the information contradicts the information which can
91 be specified in format string. */
92 #ifndef USE_IN_EXTENDED_LOCALE_MODEL
94 strfmon (char *s
, size_t maxsize
, const char *format
, ...)
97 __strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
100 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
101 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
105 # ifdef _IO_MTSAFE_IO
111 struct printf_info info
;
112 va_list ap
; /* Scan through the varargs. */
113 char *dest
; /* Pointer so copy the output. */
114 const char *fmt
; /* Pointer that walks through format. */
116 va_start (ap
, format
);
121 /* Loop through the format-string. */
124 /* The floating-point value to output. */
128 __long_double_t ldbl
;
131 int print_curr_symbol
;
145 int other_sep_by_space
;
147 int other_cs_precedes
;
148 const char *sign_string
;
149 const char *other_sign_string
;
151 const char *currency_symbol
;
152 size_t currency_symbol_len
;
158 /* Process all character which do not introduce a format
166 /* "%%" means a single '%' character. */
174 /* Defaults for formatting. */
175 print_curr_symbol
= 1; /* Print the currency symbol. */
176 left_prec
= -1; /* No left precision specified. */
177 right_prec
= -1; /* No right precision specified. */
178 group
= 1; /* Print digits grouped. */
179 pad
= ' '; /* Fill character is <SP>. */
180 is_long_double
= 0; /* Double argument by default. */
181 p_sign_posn
= -1; /* This indicates whether the */
182 n_sign_posn
= -1; /* '(' flag is given. */
183 width
= -1; /* No width specified so far. */
184 left
= 0; /* Right justified by default. */
186 /* Parse group characters. */
191 case '=': /* Set fill character. */
196 __set_errno (EINVAL
);
201 case '^': /* Don't group digits. */
204 case '+': /* Use +/- for sign of number. */
205 if (n_sign_posn
!= -1)
207 __set_errno (EINVAL
);
211 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
212 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
214 case '(': /* Use ( ) for negative sign. */
215 if (n_sign_posn
!= -1)
217 __set_errno (EINVAL
);
224 case '!': /* Don't print the currency symbol. */
225 print_curr_symbol
= 0;
227 case '-': /* Print left justified. */
231 /* Will stop the loop. */;
236 /* If not specified by the format string now find the values for
237 the format specification. */
238 if (p_sign_posn
== -1)
239 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
240 if (n_sign_posn
== -1)
241 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
245 /* Parse field width. */
246 width
= to_digit (*fmt
);
248 while (isdigit (*++fmt
))
251 width
+= to_digit (*fmt
);
254 /* If we don't have enough room for the demanded width we
255 can stop now and return an error. */
256 if (dest
+ width
>= s
+ maxsize
)
264 /* Recognize left precision. */
267 if (!isdigit (*++fmt
))
269 __set_errno (EINVAL
);
273 left_prec
= to_digit (*fmt
);
275 while (isdigit (*++fmt
))
278 left_prec
+= to_digit (*fmt
);
282 /* Recognize right precision. */
285 if (!isdigit (*++fmt
))
287 __set_errno (EINVAL
);
291 right_prec
= to_digit (*fmt
);
293 while (isdigit (*++fmt
))
296 right_prec
+= to_digit (*fmt
);
300 /* Handle modifier. This is an extension. */
307 /* Handle format specifier. */
310 case 'i': /* Use international currency symbol. */
311 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
312 currency_symbol_len
= 3;
313 space_char
= currency_symbol
[3];
314 if (right_prec
== -1)
316 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == CHAR_MAX
)
319 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
322 case 'n': /* Use national currency symbol. */
323 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
324 currency_symbol_len
= strlen (currency_symbol
);
326 if (right_prec
== -1)
328 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == CHAR_MAX
)
331 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
334 default: /* Any unrecognized format is an error. */
335 __set_errno (EINVAL
);
340 /* If we have to print the digits grouped determine how many
341 extra characters this means. */
342 if (group
&& left_prec
!= -1)
343 left_prec
+= __guess_grouping (left_prec
,
344 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
345 *_NL_CURRENT (LC_MONETARY
,
348 /* Now it's time to get the value. */
349 if (is_long_double
== 1)
351 fpnum
.ldbl
= va_arg (ap
, long double);
352 is_negative
= fpnum
.ldbl
< 0;
354 fpnum
.ldbl
= -fpnum
.ldbl
;
358 fpnum
.dbl
= va_arg (ap
, double);
359 is_negative
= fpnum
.dbl
< 0;
361 fpnum
.dbl
= -fpnum
.dbl
;
364 /* We now know the sign of the value and can determine the format. */
367 sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
368 /* If the locale does not specify a character for the
369 negative sign we use a '-'. */
370 if (*sign_string
== '\0')
371 sign_string
= (const char *) "-";
372 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
373 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
374 sign_posn
= n_sign_posn
;
376 other_sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
377 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
378 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
379 other_sign_posn
= p_sign_posn
;
383 sign_string
= _NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
384 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
385 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
386 sign_posn
= p_sign_posn
;
388 other_sign_string
= _NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
389 if (*other_sign_string
== '\0')
390 other_sign_string
= (const char *) "-";
391 other_cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
392 other_sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
393 other_sign_posn
= n_sign_posn
;
396 /* Set default values for unspecified information. */
397 if (cs_precedes
!= 0)
399 if (other_cs_precedes
!= 0)
400 other_cs_precedes
= 1;
401 if (sep_by_space
== CHAR_MAX
)
403 if (other_sep_by_space
== CHAR_MAX
)
404 other_sep_by_space
= 0;
405 if (sign_posn
== CHAR_MAX
)
407 if (other_sign_posn
== CHAR_MAX
)
410 /* Check for degenerate cases */
411 if (sep_by_space
== 2)
413 if (sign_posn
== 0 ||
414 (sign_posn
== 1 && !cs_precedes
) ||
415 (sign_posn
== 2 && cs_precedes
))
416 /* sign and symbol are not adjacent, so no separator */
419 if (other_sep_by_space
== 2)
421 if (other_sign_posn
== 0 ||
422 (other_sign_posn
== 1 && !other_cs_precedes
) ||
423 (other_sign_posn
== 2 && other_cs_precedes
))
424 /* sign and symbol are not adjacent, so no separator */
425 other_sep_by_space
= 0;
428 /* Set the left precision and padding needed for alignment */
436 /* Set left_pad to number of spaces needed to align positive
437 and negative formats */
440 int other_left_bytes
= 0;
442 /* Work out number of bytes for currency string and separator
443 preceding the value */
446 left_bytes
+= currency_symbol_len
;
447 if (sep_by_space
!= 0)
451 if (other_cs_precedes
)
453 other_left_bytes
+= currency_symbol_len
;
454 if (other_sep_by_space
!= 0)
458 /* Work out number of bytes for the sign (or left parenthesis)
459 preceding the value */
460 if (sign_posn
== 0 && is_negative
)
462 else if (sign_posn
== 1)
463 left_bytes
+= strlen (sign_string
);
464 else if (cs_precedes
&& (sign_posn
== 3 || sign_posn
== 4))
465 left_bytes
+= strlen (sign_string
);
467 if (other_sign_posn
== 0 && !is_negative
)
469 else if (other_sign_posn
== 1)
470 other_left_bytes
+= strlen (other_sign_string
);
471 else if (other_cs_precedes
&&
472 (other_sign_posn
== 3 || other_sign_posn
== 4))
473 other_left_bytes
+= strlen (other_sign_string
);
475 /* Compare the number of bytes preceding the value for
476 each format, and set the padding accordingly */
477 if (other_left_bytes
> left_bytes
)
478 left_pad
= other_left_bytes
- left_bytes
;
483 /* Perhaps we'll someday make these things configurable so
484 better start using symbolic names now. */
485 #define left_paren '('
486 #define right_paren ')'
488 startp
= dest
; /* Remember start so we can compute length. */
490 while (left_pad
-- > 0)
493 if (sign_posn
== 0 && is_negative
)
494 out_char (left_paren
);
498 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
501 out_string (sign_string
);
502 if (sep_by_space
== 2)
506 if (print_curr_symbol
)
508 out_string (currency_symbol
);
512 if (sep_by_space
== 2)
513 out_char (space_char
);
514 out_string (sign_string
);
515 if (sep_by_space
== 1)
516 /* POSIX.2 and SUS are not clear on this case, but C99
517 says a space follows the adjacent-symbol-and-sign */
521 if (sep_by_space
== 1)
522 out_char (space_char
);
526 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
527 && sign_posn
!= 4 && sign_posn
!= 5)
528 out_string (sign_string
);
530 /* Print the number. */
532 # ifdef _IO_MTSAFE_IO
533 f
._sbf
._f
._lock
= &lock
;
535 INTUSE(_IO_init
) ((_IO_FILE
*) &f
, 0);
536 _IO_JUMPS ((struct _IO_FILE_plus
*) &f
) = &_IO_str_jumps
;
537 INTUSE(_IO_str_init_static
) ((_IO_strfile
*) &f
, dest
,
538 (s
+ maxsize
) - dest
, dest
);
540 memset ((void *) &f
, 0, sizeof (f
));
541 f
.__magic
= _IOMAGIC
;
542 f
.__mode
.__write
= 1;
543 /* The buffer size is one less than MAXLEN
544 so we have space for the null terminator. */
545 f
.__bufp
= f
.__buffer
= (char *) dest
;
546 f
.__bufsize
= (s
+ maxsize
) - dest
;
547 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
548 f
.__get_limit
= f
.__buffer
;
549 /* After the buffer is full (MAXLEN characters have been written),
550 any more characters written will go to the bit bucket. */
551 f
.__room_funcs
= __default_room_functions
;
552 f
.__io_funcs
.__write
= NULL
;
555 /* We clear the last available byte so we can find out whether
556 the numeric representation is too long. */
557 s
[maxsize
- 1] = '\0';
559 info
.prec
= right_prec
;
560 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
562 info
.is_long_double
= is_long_double
;
571 info
.extra
= 1; /* This means use values from LC_MONETARY. */
575 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
582 if (s
[maxsize
- 1] != '\0')
594 if (sep_by_space
== 1)
596 out_string (sign_string
);
599 if (print_curr_symbol
)
601 if ((sign_posn
== 3 && sep_by_space
== 2)
602 || (sign_posn
== 4 && sep_by_space
== 1)
603 || (sign_posn
== 2 && sep_by_space
== 1)
604 || (sign_posn
== 1 && sep_by_space
== 1)
605 || (sign_posn
== 0 && sep_by_space
== 1))
606 out_char (space_char
);
607 out_nstring (currency_symbol
, currency_symbol_len
);
610 if (sep_by_space
== 2)
612 out_string (sign_string
);
619 if (sep_by_space
== 2)
621 out_string (sign_string
);
624 if (sign_posn
== 0 && is_negative
)
625 out_char (right_paren
);
627 /* Now test whether the output width is filled. */
628 if (dest
- startp
< width
)
631 /* We simply have to fill using spaces. */
634 while (dest
- startp
< width
);
637 int dist
= width
- (dest
- startp
);
639 for (cp
= dest
- 1; cp
>= startp
; --cp
)
645 startp
[--dist
] = ' ';
651 /* Terminate the string. */