1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996, 1997 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 to_digit(Ch) ((Ch) - '0')
58 /* We use this code also for the extended locale handling where the
59 function gets as an additional argument the locale which has to be
60 used. To access the values we have to redefine the _NL_CURRENT
62 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
64 # define _NL_CURRENT(category, item) \
65 (current->values[_NL_ITEM_INDEX (item)].string)
68 extern int __printf_fp (FILE *, const struct printf_info
*,
70 /* This function determines the number of digit groups in the output.
71 The definition is in printf_fp.c. */
72 extern unsigned int __guess_grouping (unsigned int intdig_max
,
73 const char *grouping
, wchar_t sepchar
);
76 /* We have to overcome some problems with this implementation. On the
77 one hand the strfmon() function is specified in XPG4 and of course
78 it has to follow this. But on the other hand POSIX.2 specifies
79 some information in the LC_MONETARY category which should be used,
80 too. Some of the information contradicts the information which can
81 be specified in format string. */
82 #ifndef USE_IN_EXTENDED_LOCALE_MODEL
84 strfmon (char *s
, size_t maxsize
, const char *format
, ...)
87 __strfmon_l (char *s
, size_t maxsize
, __locale_t loc
, const char *format
, ...)
90 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
91 struct locale_data
*current
= loc
->__locales
[LC_MONETARY
];
98 struct printf_info info
;
99 va_list ap
; /* Scan through the varargs. */
100 char *dest
; /* Pointer so copy the output. */
101 const char *fmt
; /* Pointer that walks through format. */
103 va_start (ap
, format
);
108 /* Loop through the format-string. */
111 /* The floating-point value to output. */
115 __long_double_t ldbl
;
118 int print_curr_symbol
;
133 const char *currency_symbol
;
138 /* Process all character which do not introduce a format
146 /* "%%" means a single '%' character. */
154 /* Defaults for formatting. */
155 print_curr_symbol
= 1; /* Print the currency symbol. */
156 left_prec
= -1; /* No left precision specified. */
157 right_prec
= -1; /* No right precision specified. */
158 group
= 1; /* Print digits grouped. */
159 pad
= ' '; /* Fill character is <SP>. */
160 is_long_double
= 0; /* Double argument by default. */
161 p_sign_posn
= -1; /* This indicates whether the */
162 n_sign_posn
= -1; /* '(' flag is given. */
163 width
= -1; /* No width specified so far. */
164 left
= 0; /* Right justified by default. */
166 /* Parse group characters. */
171 case '=': /* Set fill character. */
174 case '^': /* Don't group digits. */
177 case '+': /* Use +/- for sign of number. */
178 if (n_sign_posn
!= -1)
180 __set_errno (EINVAL
);
184 if (*_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
) == '\0')
187 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
188 if (*_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
) == '\0')
191 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
193 case '(': /* Use ( ) for negative sign. */
194 if (n_sign_posn
!= -1)
196 __set_errno (EINVAL
);
200 n_sign_posn
= 5; /* This is a else unused value. */
202 case '!': /* Don't print the currency symbol. */
203 print_curr_symbol
= 0;
205 case '-': /* Print left justified. */
209 /* Will stop the loop. */;
216 /* Parse field width. */
217 width
= to_digit (*fmt
);
219 while (isdigit (*++fmt
))
222 width
+= to_digit (*fmt
);
225 /* If we don't have enough room for the demanded width we
226 can stop now and return an error. */
227 if (dest
+ width
>= s
+ maxsize
)
235 /* Recognize left precision. */
238 if (!isdigit (*++fmt
))
240 __set_errno (EINVAL
);
244 left_prec
= to_digit (*fmt
);
246 while (isdigit (*++fmt
))
249 left_prec
+= to_digit (*fmt
);
253 /* Recognize right precision. */
256 if (!isdigit (*++fmt
))
258 __set_errno (EINVAL
);
262 right_prec
= to_digit (*fmt
);
264 while (isdigit (*++fmt
))
267 right_prec
+= to_digit (*fmt
);
271 /* Handle modifier. This is an extension. */
278 /* Handle format specifier. */
281 case 'i': /* Use international currency symbol. */
282 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
283 if (right_prec
== -1)
284 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == '\177')
287 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
289 case 'n': /* Use national currency symbol. */
290 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
291 if (right_prec
== -1)
292 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == '\177')
295 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
297 default: /* Any unrecognized format is an error. */
298 __set_errno (EINVAL
);
303 /* If we have to print the digits grouped determine how many
304 extra characters this means. */
305 if (group
&& left_prec
!= -1)
306 left_prec
+= __guess_grouping (left_prec
,
307 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
308 *_NL_CURRENT (LC_MONETARY
,
311 /* Now it's time to get the value. */
312 if (is_long_double
== 1)
314 fpnum
.ldbl
= va_arg (ap
, long double);
315 is_negative
= fpnum
.ldbl
< 0;
317 fpnum
.ldbl
= -fpnum
.ldbl
;
321 fpnum
.dbl
= va_arg (ap
, double);
322 is_negative
= fpnum
.dbl
< 0;
324 fpnum
.dbl
= -fpnum
.dbl
;
327 /* We now know the sign of the value and can determine the format. */
330 sign_char
= *_NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
331 /* If the locale does not specify a character for the
332 negative sign we use a '-'. */
333 if (sign_char
== '\0')
335 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
336 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
337 /* If the '(' flag is not given use the sign position from
338 the current locale. */
339 if (n_sign_posn
== -1)
340 sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
342 /* This means use parentheses. */
347 sign_char
= *_NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
348 /* If the locale does not specify a character for the
349 positive sign we use a <SP>. */
350 if (sign_char
== '\0')
352 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
353 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
354 if (n_sign_posn
== -1)
355 sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
357 /* Here we don't set SIGN_POSN to 0 because we don'want to
358 print <SP> instead of the braces and this is what the
363 /* Set default values for unspecified information. */
364 if (cs_precedes
!= 0)
366 if (sep_by_space
== 127)
372 /* Perhaps we'll someday make these things configurable so
373 better start using symbolic names now. */
374 #define left_paren '('
375 #define right_paren ')'
377 startp
= dest
; /* Remember start so we can compute length. */
380 out_char (left_paren
);
381 if (sign_posn
== 5) /* This is for positive number and ( flag. */
386 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
389 out_char (sign_char
);
390 if (sep_by_space
== 2)
394 if (print_curr_symbol
)
396 out_string (currency_symbol
);
400 if (sep_by_space
== 2)
402 out_char (sign_char
);
405 if (sep_by_space
== 1)
410 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
411 && sign_posn
!= 4 && sign_posn
!= 5)
412 out_char (sign_char
);
414 /* Print the number. */
416 _IO_init ((_IO_FILE
*) &f
, 0);
417 _IO_JUMPS ((_IO_FILE
*) &f
) = &_IO_str_jumps
;
418 _IO_str_init_static ((_IO_FILE
*) &f
, dest
, (s
+ maxsize
) - dest
, dest
);
420 memset((void *) &f
, 0, sizeof(f
));
421 f
.__magic
= _IOMAGIC
;
422 f
.__mode
.__write
= 1;
423 /* The buffer size is one less than MAXLEN
424 so we have space for the null terminator. */
425 f
.__bufp
= f
.__buffer
= (char *) dest
;
426 f
.__bufsize
= (s
+ maxsize
) - dest
;
427 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
428 f
.__get_limit
= f
.__buffer
;
429 /* After the buffer is full (MAXLEN characters have been written),
430 any more characters written will go to the bit bucket. */
431 f
.__room_funcs
= __default_room_functions
;
432 f
.__io_funcs
.__write
= NULL
;
435 /* We clear the last available byte so we can find out whether
436 the numeric representation is too long. */
437 s
[maxsize
- 1] = '\0';
439 info
.prec
= right_prec
;
440 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
442 info
.is_long_double
= is_long_double
;
451 info
.extra
= 1; /* This means use values from LC_MONETARY. */
454 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
461 if (s
[maxsize
- 1] != '\0')
470 if (sep_by_space
== 1)
472 out_char (sign_char
);
475 if (print_curr_symbol
)
477 if (sign_posn
== 3 && sep_by_space
== 2)
479 out_string (currency_symbol
);
485 if (sep_by_space
== 2)
487 out_char (sign_char
);
491 out_char (right_paren
);
493 out_char (' '); /* This is for positive number and ( flag. */
495 /* Now test whether the output width is filled. */
496 if (dest
- startp
< width
)
498 /* We simply have to fill using spaces. */
501 while (dest
- startp
< width
);
504 int dist
= width
- (dest
- startp
);
506 for (cp
= dest
- 1; cp
>= startp
; --cp
)
512 startp
[--dist
] = ' ';
517 /* Terminate the string. */