1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996, 1997, 1998 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')
57 extern int __printf_fp (FILE *, const struct printf_info
*,
59 /* This function determines the number of digit groups in the output.
60 The definition is in printf_fp.c. */
61 extern unsigned int __guess_grouping (unsigned int intdig_max
,
62 const char *grouping
, wchar_t sepchar
);
65 /* We have to overcome some problems with this implementation. On the
66 one hand the strfmon() function is specified by in XPG4 and of
67 course it has to follow this. But on the other hand POSIX.2
68 specifies some information in the LC_MONETARY category which should
69 be used, too. Some of the information contradicts the information
70 which can be specified in format string. */
72 strfmon (char *s
, size_t maxsize
, const char *format
, ...)
79 struct printf_info info
;
80 va_list ap
; /* Scan through the varargs. */
81 char *dest
; /* Pointer so copy the output. */
82 const char *fmt
; /* Pointer that walks through format. */
84 va_start (ap
, format
);
89 /* Loop through the format-string. */
92 /* The floating-point value to output. */
99 int print_curr_symbol
;
114 const char *currency_symbol
;
119 /* Process all character which do not introduce a format
127 /* "%%" means a single '%' character. */
135 /* Defaults for formatting. */
136 print_curr_symbol
= 1; /* Print the currency symbol. */
137 left_prec
= -1; /* No left precision specified. */
138 right_prec
= -1; /* No right precision specified. */
139 group
= 1; /* Print digits grouped. */
140 pad
= ' '; /* Fill character is <SP>. */
141 is_long_double
= 0; /* Double argument by default. */
142 p_sign_posn
= -1; /* This indicates whether the */
143 n_sign_posn
= -1; /* '(' flag is given. */
144 width
= -1; /* No width specified so far. */
145 left
= 0; /* Right justified by default. */
147 /* Parse group characters. */
152 case '=': /* Set fill character. */
157 __set_errno (EINVAL
);
162 case '^': /* Don't group digits. */
165 case '+': /* Use +/- for sign of number. */
166 if (n_sign_posn
!= -1)
168 __set_errno (EINVAL
);
172 if (*_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
) == '\0')
175 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
176 if (*_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
) == '\0')
179 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
181 case '(': /* Use ( ) for negative sign. */
182 if (n_sign_posn
!= -1)
184 __set_errno (EINVAL
);
191 case '!': /* Don't print the currency symbol. */
192 print_curr_symbol
= 0;
194 case '-': /* Print left justified. */
198 /* Will stop the loop. */;
203 /* If not specified by the format string now find the values for
204 the format specification. */
205 if (p_sign_posn
== -1)
206 p_sign_posn
= *_NL_CURRENT (LC_MONETARY
, P_SIGN_POSN
);
207 if (n_sign_posn
== -1)
208 n_sign_posn
= *_NL_CURRENT (LC_MONETARY
, N_SIGN_POSN
);
212 /* Parse field width. */
213 width
= to_digit (*fmt
);
215 while (isdigit (*++fmt
))
218 width
+= to_digit (*fmt
);
221 /* If we don't have enough room for the demanded width we
222 can stop now and return an error. */
223 if (dest
+ width
>= s
+ maxsize
)
231 /* Recognize left precision. */
234 if (!isdigit (*++fmt
))
236 __set_errno (EINVAL
);
240 left_prec
= to_digit (*fmt
);
242 while (isdigit (*++fmt
))
245 left_prec
+= to_digit (*fmt
);
249 /* Recognize right precision. */
252 if (!isdigit (*++fmt
))
254 __set_errno (EINVAL
);
258 right_prec
= to_digit (*fmt
);
260 while (isdigit (*++fmt
))
263 right_prec
+= to_digit (*fmt
);
267 /* Handle modifier. This is an extension. */
274 /* Handle format specifier. */
277 case 'i': /* Use international currency symbol. */
278 currency_symbol
= _NL_CURRENT (LC_MONETARY
, INT_CURR_SYMBOL
);
279 if (right_prec
== -1)
280 if (*_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
) == '\177')
283 right_prec
= *_NL_CURRENT (LC_MONETARY
, INT_FRAC_DIGITS
);
285 case 'n': /* Use national currency symbol. */
286 currency_symbol
= _NL_CURRENT (LC_MONETARY
, CURRENCY_SYMBOL
);
287 if (right_prec
== -1)
288 if (*_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
) == '\177')
291 right_prec
= *_NL_CURRENT (LC_MONETARY
, FRAC_DIGITS
);
293 default: /* Any unrecognized format is an error. */
294 __set_errno (EINVAL
);
299 /* If we have to print the digits grouped determine how many
300 extra characters this means. */
301 if (group
&& left_prec
!= -1)
302 left_prec
+= __guess_grouping (left_prec
,
303 _NL_CURRENT (LC_MONETARY
, MON_GROUPING
),
304 *_NL_CURRENT (LC_MONETARY
,
307 /* Now it's time to get the value. */
308 if (is_long_double
== 1)
310 fpnum
.ldbl
= va_arg (ap
, long double);
311 is_negative
= fpnum
.ldbl
< 0;
313 fpnum
.ldbl
= -fpnum
.ldbl
;
317 fpnum
.dbl
= va_arg (ap
, double);
318 is_negative
= fpnum
.dbl
< 0;
320 fpnum
.dbl
= -fpnum
.dbl
;
323 /* We now know the sign of the value and can determine the format. */
326 sign_char
= *_NL_CURRENT (LC_MONETARY
, NEGATIVE_SIGN
);
327 /* If the locale does not specify a character for the
328 negative sign we use a '-'. */
329 if (sign_char
== '\0')
331 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, N_CS_PRECEDES
);
332 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, N_SEP_BY_SPACE
);
333 sign_posn
= n_sign_posn
;
337 sign_char
= *_NL_CURRENT (LC_MONETARY
, POSITIVE_SIGN
);
338 /* If the locale does not specify a character for the
339 positive sign we use a <SP>. */
340 if (sign_char
== '\0')
342 cs_precedes
= *_NL_CURRENT (LC_MONETARY
, P_CS_PRECEDES
);
343 sep_by_space
= *_NL_CURRENT (LC_MONETARY
, P_SEP_BY_SPACE
);
344 sign_posn
= p_sign_posn
;
347 /* Set default values for unspecified information. */
348 if (cs_precedes
!= 0)
350 if (sep_by_space
== 127)
356 /* Perhaps we'll someday make these things configurable so
357 better start using symbolic names now. */
358 #define left_paren '('
359 #define right_paren ')'
361 startp
= dest
; /* Remember start so we can compute length. */
364 out_char (is_negative
? left_paren
: ' ');
368 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 4
371 out_char (sign_char
);
372 if (sep_by_space
== 2)
376 if (print_curr_symbol
)
378 out_string (currency_symbol
);
382 if (sep_by_space
== 2)
384 out_char (sign_char
);
387 if (sep_by_space
== 1)
392 if (sign_posn
!= 0 && sign_posn
!= 2 && sign_posn
!= 3
393 && sign_posn
!= 4 && sign_posn
!= 5)
394 out_char (sign_char
);
396 /* Print the number. */
398 _IO_init ((_IO_FILE
*) &f
, 0);
399 _IO_JUMPS ((_IO_FILE
*) &f
) = &_IO_str_jumps
;
400 _IO_str_init_static ((_IO_FILE
*) &f
, dest
, (s
+ maxsize
) - dest
, dest
);
402 memset((void *) &f
, 0, sizeof(f
));
403 f
.__magic
= _IOMAGIC
;
404 f
.__mode
.__write
= 1;
405 /* The buffer size is one less than MAXLEN
406 so we have space for the null terminator. */
407 f
.__bufp
= f
.__buffer
= (char *) dest
;
408 f
.__bufsize
= (s
+ maxsize
) - dest
;
409 f
.__put_limit
= f
.__buffer
+ f
.__bufsize
;
410 f
.__get_limit
= f
.__buffer
;
411 /* After the buffer is full (MAXLEN characters have been written),
412 any more characters written will go to the bit bucket. */
413 f
.__room_funcs
= __default_room_functions
;
414 f
.__io_funcs
.__write
= NULL
;
417 /* We clear the last available byte so we can find out whether
418 the numeric representation is too long. */
419 s
[maxsize
- 1] = '\0';
421 info
.prec
= right_prec
;
422 info
.width
= left_prec
+ (right_prec
? (right_prec
+ 1) : 0);
424 info
.is_long_double
= is_long_double
;
433 info
.extra
= 1; /* This means use values from LC_MONETARY. */
436 done
= __printf_fp ((FILE *) &f
, &info
, &ptr
);
443 if (s
[maxsize
- 1] != '\0')
452 if (sep_by_space
== 1)
454 out_char (sign_char
);
457 if (print_curr_symbol
)
459 if (sign_posn
== 3 && sep_by_space
== 2)
461 out_string (currency_symbol
);
467 if (sep_by_space
== 2)
469 out_char (sign_char
);
473 out_char (is_negative
? right_paren
: ' ');
475 /* Now test whether the output width is filled. */
476 if (dest
- startp
< width
)
478 /* We simply have to fill using spaces. */
481 while (dest
- startp
< width
);
484 int dist
= width
- (dest
- startp
);
486 for (cp
= dest
- 1; cp
>= startp
; --cp
)
492 startp
[--dist
] = ' ';
497 /* Terminate the string. */