1 /* strfmon -- formating a monetary value according to the current locale
2 Copyright (C) 1996 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
19 not, 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. */
155 case '^': /* Don't group digits. */
158 case '+': /* Use +/- for sign of number. */
159 if (n_sign_posn != -1)
161 __set_errno (EINVAL);
165 if (*_NL_CURRENT (LC_MONETARY, P_SIGN_POSN) == '\0')
168 p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
169 if (*_NL_CURRENT (LC_MONETARY, N_SIGN_POSN) == '\0')
172 n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
174 case '(': /* Use ( ) for negative sign. */
175 if (n_sign_posn != -1)
177 __set_errno (EINVAL);
181 n_sign_posn = 5; /* This is a else unused value. */
183 case '!': /* Don't print the currency symbol. */
184 print_curr_symbol = 0;
186 case '-': /* Print left justified. */
190 /* Will stop the loop. */;
197 /* Parse field width. */
198 width = to_digit (*fmt);
200 while (isdigit (*++fmt))
203 width += to_digit (*fmt);
206 /* If we don't have enough room for the demanded width we
207 can stop now and return an error. */
208 if (dest + width >= s + maxsize)
216 /* Recognize left precision. */
219 if (!isdigit (*++fmt))
221 __set_errno (EINVAL);
225 left_prec = to_digit (*fmt);
227 while (isdigit (*++fmt))
230 left_prec += to_digit (*fmt);
234 /* Recognize right precision. */
237 if (!isdigit (*++fmt))
239 __set_errno (EINVAL);
243 right_prec = to_digit (*fmt);
245 while (isdigit (*++fmt))
248 right_prec += to_digit (*fmt);
252 /* Handle modifier. This is an extension. */
259 /* Handle format specifier. */
262 case 'i': /* Use international currency symbol. */
263 currency_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL);
264 if (right_prec == -1)
265 if (*_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS) == '\177')
268 right_prec = *_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS);
270 case 'n': /* Use national currency symbol. */
271 currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL);
272 if (right_prec == -1)
273 if (*_NL_CURRENT (LC_MONETARY, FRAC_DIGITS) == '\177')
276 right_prec = *_NL_CURRENT (LC_MONETARY, FRAC_DIGITS);
278 default: /* Any unrecognized format is an error. */
279 __set_errno (EINVAL);
284 /* If we have to print the digits grouped determine how many
285 extra characters this means. */
286 if (group && left_prec != -1)
287 left_prec += __guess_grouping (left_prec,
288 _NL_CURRENT (LC_MONETARY, MON_GROUPING),
289 *_NL_CURRENT (LC_MONETARY,
292 /* Now it's time to get the value. */
293 if (is_long_double == 1)
295 fpnum.ldbl = va_arg (ap, long double);
296 is_negative = fpnum.ldbl < 0;
298 fpnum.ldbl = -fpnum.ldbl;
302 fpnum.dbl = va_arg (ap, double);
303 is_negative = fpnum.dbl < 0;
305 fpnum.dbl = -fpnum.dbl;
308 /* We now know the sign of the value and can determine the format. */
311 sign_char = *_NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
312 /* If the locale does not specify a character for the
313 negative sign we use a '-'. */
314 if (sign_char == '\0')
316 cs_precedes = *_NL_CURRENT (LC_MONETARY, N_CS_PRECEDES);
317 sep_by_space = *_NL_CURRENT (LC_MONETARY, N_SEP_BY_SPACE);
318 /* If the '(' flag is not given use the sign position from
319 the current locale. */
320 if (n_sign_posn == -1)
321 sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
323 /* This means use parentheses. */
328 sign_char = *_NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
329 /* If the locale does not specify a character for the
330 positive sign we use a <SP>. */
331 if (sign_char == '\0')
333 cs_precedes = *_NL_CURRENT (LC_MONETARY, P_CS_PRECEDES);
334 sep_by_space = *_NL_CURRENT (LC_MONETARY, P_SEP_BY_SPACE);
335 if (n_sign_posn == -1)
336 sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
338 /* Here we don't set SIGN_POSN to 0 because we don'want to
339 print <SP> instead of the braces and this is what the
344 /* Set default values for unspecified information. */
345 if (cs_precedes != 0)
347 if (sep_by_space == 127)
353 /* Perhaps we'll someday make these things configurable so
354 better start using symbolic names now. */
355 #define left_paren '('
356 #define right_paren ')'
358 startp = dest; /* Remember start so we can compute lenght. */
361 out_char (left_paren);
362 if (sign_posn == 5) /* This is for positive number and ( flag. */
367 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
370 out_char (sign_char);
371 if (sep_by_space == 2)
375 if (print_curr_symbol)
377 out_string (currency_symbol);
381 if (sep_by_space == 2)
383 out_char (sign_char);
386 if (sep_by_space == 1)
391 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
392 && sign_posn != 4 && sign_posn != 5)
393 out_char (sign_char);
395 /* Print the number. */
397 _IO_init ((_IO_FILE *) &f, 0);
398 _IO_JUMPS ((_IO_FILE *) &f) = &_IO_str_jumps;
399 _IO_str_init_static ((_IO_FILE *) &f, dest, (s + maxsize) - dest, dest);
401 memset((void *) &f, 0, sizeof(f));
402 f.__magic = _IOMAGIC;
403 f.__mode.__write = 1;
404 /* The buffer size is one less than MAXLEN
405 so we have space for the null terminator. */
406 f.__bufp = f.__buffer = (char *) dest;
407 f.__bufsize = (s + maxsize) - dest;
408 f.__put_limit = f.__buffer + f.__bufsize;
409 f.__get_limit = f.__buffer;
410 /* After the buffer is full (MAXLEN characters have been written),
411 any more characters written will go to the bit bucket. */
412 f.__room_funcs = __default_room_functions;
413 f.__io_funcs.__write = NULL;
416 /* We clear the last available byte so we can find out whether
417 the numeric representation is too long. */
418 s[maxsize - 1] = '\0';
420 info.prec = right_prec;
421 info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
423 info.is_long_double = is_long_double;
432 info.extra = 1; /* This means use values from LC_MONETARY. */
435 done = __printf_fp ((FILE *) &f, &info, &ptr);
442 if (s[maxsize - 1] != '\0')
451 if (sep_by_space == 1)
453 out_char (sign_char);
456 if (print_curr_symbol)
458 if (sign_posn == 3 && sep_by_space == 2)
460 out_string (currency_symbol);
466 if (sep_by_space == 2)
468 out_char (sign_char);
472 out_char (right_paren);
474 out_char (' '); /* This is for positive number and ( flag. */
476 /* Now test whether the output width is filled. */
477 if (dest - startp < width)
479 /* We simply have to fill using spaces. */
482 while (dest - startp < width);
485 int dist = width - (dest - startp);
487 for (cp = dest - 1; cp >= startp; --cp)
493 startp[--dist] = ' ';
498 /* Terminate the string. */