Update.
[glibc.git] / stdlib / strfmon.c
blob319736dc82adb99193e5be8e90baf8569e32fa81
1 /* Formatting a monetary value according to the current locale.
2 Copyright (C) 1996, 1997, 1998, 1999, 2000 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. */
22 #include <ctype.h>
23 #include <errno.h>
24 #include <langinfo.h>
25 #include <monetary.h>
26 #ifdef USE_IN_LIBIO
27 # include "../libio/libioP.h"
28 # include "../libio/strfile.h"
29 #endif
30 #include <printf.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include "../locale/localeinfo.h"
37 #define out_char(Ch) \
38 do { \
39 if (dest >= s + maxsize - 1) \
40 { \
41 __set_errno (E2BIG); \
42 va_end (ap); \
43 return -1; \
44 } \
45 *dest++ = (Ch); \
46 } while (0)
48 #define out_string(String) \
49 do { \
50 const char *_s = (String); \
51 while (*_s) \
52 out_char (*_s++); \
53 } while (0)
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
61 macro. */
62 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
63 # undef _NL_CURRENT
64 # define _NL_CURRENT(category, item) \
65 (current->values[_NL_ITEM_INDEX (item)].string)
66 #endif
68 extern int __printf_fp (FILE *, const struct printf_info *,
69 const void **const);
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
83 ssize_t
84 strfmon (char *s, size_t maxsize, const char *format, ...)
85 #else
86 ssize_t
87 __strfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, ...)
88 #endif
90 #ifdef USE_IN_EXTENDED_LOCALE_MODEL
91 struct locale_data *current = loc->__locales[LC_MONETARY];
92 #endif
93 #ifdef USE_IN_LIBIO
94 _IO_strfile f;
95 #else
96 FILE f;
97 #endif
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);
105 dest = s;
106 fmt = format;
108 /* Loop through the format-string. */
109 while (*fmt != '\0')
111 /* The floating-point value to output. */
112 union
114 double dbl;
115 __long_double_t ldbl;
117 fpnum;
118 int print_curr_symbol;
119 int left_prec;
120 int left_pad;
121 int right_prec;
122 int group;
123 char pad;
124 int is_long_double;
125 int p_sign_posn;
126 int n_sign_posn;
127 int sign_posn;
128 int other_sign_posn;
129 int left;
130 int is_negative;
131 int sep_by_space;
132 int other_sep_by_space;
133 int cs_precedes;
134 int other_cs_precedes;
135 const char *sign_string;
136 const char *other_sign_string;
137 int done;
138 const char *currency_symbol;
139 int width;
140 char *startp;
141 const void *ptr;
143 /* Process all character which do not introduce a format
144 specification. */
145 if (*fmt != '%')
147 out_char (*fmt++);
148 continue;
151 /* "%%" means a single '%' character. */
152 if (fmt[1] == '%')
154 out_char (*++fmt);
155 ++fmt;
156 continue;
159 /* Defaults for formatting. */
160 print_curr_symbol = 1; /* Print the currency symbol. */
161 left_prec = -1; /* No left precision specified. */
162 right_prec = -1; /* No right precision specified. */
163 group = 1; /* Print digits grouped. */
164 pad = ' '; /* Fill character is <SP>. */
165 is_long_double = 0; /* Double argument by default. */
166 p_sign_posn = -1; /* This indicates whether the */
167 n_sign_posn = -1; /* '(' flag is given. */
168 width = -1; /* No width specified so far. */
169 left = 0; /* Right justified by default. */
171 /* Parse group characters. */
172 while (1)
174 switch (*++fmt)
176 case '=': /* Set fill character. */
177 pad = *++fmt;
178 if (pad == '\0')
180 /* Premature EOS. */
181 __set_errno (EINVAL);
182 va_end (ap);
183 return -1;
185 continue;
186 case '^': /* Don't group digits. */
187 group = 0;
188 continue;
189 case '+': /* Use +/- for sign of number. */
190 if (n_sign_posn != -1)
192 __set_errno (EINVAL);
193 va_end (ap);
194 return -1;
196 p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
197 n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
198 continue;
199 case '(': /* Use ( ) for negative sign. */
200 if (n_sign_posn != -1)
202 __set_errno (EINVAL);
203 va_end (ap);
204 return -1;
206 p_sign_posn = 0;
207 n_sign_posn = 0;
208 continue;
209 case '!': /* Don't print the currency symbol. */
210 print_curr_symbol = 0;
211 continue;
212 case '-': /* Print left justified. */
213 left = 1;
214 continue;
215 default:
216 /* Will stop the loop. */;
218 break;
221 /* If not specified by the format string now find the values for
222 the format specification. */
223 if (p_sign_posn == -1)
224 p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
225 if (n_sign_posn == -1)
226 n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
228 if (isdigit (*fmt))
230 /* Parse field width. */
231 width = to_digit (*fmt);
233 while (isdigit (*++fmt))
235 width *= 10;
236 width += to_digit (*fmt);
239 /* If we don't have enough room for the demanded width we
240 can stop now and return an error. */
241 if (dest + width >= s + maxsize)
243 __set_errno (E2BIG);
244 va_end (ap);
245 return -1;
249 /* Recognize left precision. */
250 if (*fmt == '#')
252 if (!isdigit (*++fmt))
254 __set_errno (EINVAL);
255 va_end (ap);
256 return -1;
258 left_prec = to_digit (*fmt);
260 while (isdigit (*++fmt))
262 left_prec *= 10;
263 left_prec += to_digit (*fmt);
267 /* Recognize right precision. */
268 if (*fmt == '.')
270 if (!isdigit (*++fmt))
272 __set_errno (EINVAL);
273 va_end (ap);
274 return -1;
276 right_prec = to_digit (*fmt);
278 while (isdigit (*++fmt))
280 right_prec *= 10;
281 right_prec += to_digit (*fmt);
285 /* Handle modifier. This is an extension. */
286 if (*fmt == 'L')
288 ++fmt;
289 is_long_double = 1;
292 /* Handle format specifier. */
293 switch (*fmt++)
295 case 'i': /* Use international currency symbol. */
296 currency_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL);
297 if (right_prec == -1)
299 if (*_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS) == CHAR_MAX)
300 right_prec = 2;
301 else
302 right_prec = *_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS);
304 break;
305 case 'n': /* Use national currency symbol. */
306 currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL);
307 if (right_prec == -1)
309 if (*_NL_CURRENT (LC_MONETARY, FRAC_DIGITS) == CHAR_MAX)
310 right_prec = 2;
311 else
312 right_prec = *_NL_CURRENT (LC_MONETARY, FRAC_DIGITS);
314 break;
315 default: /* Any unrecognized format is an error. */
316 __set_errno (EINVAL);
317 va_end (ap);
318 return -1;
321 /* If we have to print the digits grouped determine how many
322 extra characters this means. */
323 if (group && left_prec != -1)
324 left_prec += __guess_grouping (left_prec,
325 _NL_CURRENT (LC_MONETARY, MON_GROUPING),
326 *_NL_CURRENT (LC_MONETARY,
327 MON_THOUSANDS_SEP));
329 /* Now it's time to get the value. */
330 if (is_long_double == 1)
332 fpnum.ldbl = va_arg (ap, long double);
333 is_negative = fpnum.ldbl < 0;
334 if (is_negative)
335 fpnum.ldbl = -fpnum.ldbl;
337 else
339 fpnum.dbl = va_arg (ap, double);
340 is_negative = fpnum.dbl < 0;
341 if (is_negative)
342 fpnum.dbl = -fpnum.dbl;
345 /* We now know the sign of the value and can determine the format. */
346 if (is_negative)
348 sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
349 /* If the locale does not specify a character for the
350 negative sign we use a '-'. */
351 if (*sign_string == '\0')
352 sign_string = (const char *) "-";
353 cs_precedes = *_NL_CURRENT (LC_MONETARY, N_CS_PRECEDES);
354 sep_by_space = *_NL_CURRENT (LC_MONETARY, N_SEP_BY_SPACE);
355 sign_posn = n_sign_posn;
357 other_sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
358 other_cs_precedes = *_NL_CURRENT (LC_MONETARY, P_CS_PRECEDES);
359 other_sep_by_space = *_NL_CURRENT (LC_MONETARY, P_SEP_BY_SPACE);
360 other_sign_posn = p_sign_posn;
362 else
364 sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
365 cs_precedes = *_NL_CURRENT (LC_MONETARY, P_CS_PRECEDES);
366 sep_by_space = *_NL_CURRENT (LC_MONETARY, P_SEP_BY_SPACE);
367 sign_posn = p_sign_posn;
369 other_sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
370 if (*other_sign_string == '\0')
371 other_sign_string = (const char *) "-";
372 other_cs_precedes = *_NL_CURRENT (LC_MONETARY, N_CS_PRECEDES);
373 other_sep_by_space = *_NL_CURRENT (LC_MONETARY, N_SEP_BY_SPACE);
374 other_sign_posn = n_sign_posn;
377 /* Set default values for unspecified information. */
378 if (cs_precedes != 0)
379 cs_precedes = 1;
380 if (other_cs_precedes != 0)
381 other_cs_precedes = 1;
382 if (sep_by_space == CHAR_MAX)
383 sep_by_space = 0;
384 if (other_sep_by_space == CHAR_MAX)
385 other_sep_by_space = 0;
386 if (sign_posn == CHAR_MAX)
387 sign_posn = 1;
388 if (other_sign_posn == CHAR_MAX)
389 other_sign_posn = 1;
391 /* Check for degenerate cases */
392 if (sep_by_space == 2)
394 if (sign_posn == 0 ||
395 (sign_posn == 1 && !cs_precedes) ||
396 (sign_posn == 2 && cs_precedes))
397 /* sign and symbol are not adjacent, so no separator */
398 sep_by_space = 0;
400 if (other_sep_by_space == 2)
402 if (other_sign_posn == 0 ||
403 (other_sign_posn == 1 && !other_cs_precedes) ||
404 (other_sign_posn == 2 && other_cs_precedes))
405 /* sign and symbol are not adjacent, so no separator */
406 other_sep_by_space = 0;
409 /* Set the left precision and padding needed for alignment */
410 if (left_prec == -1)
412 left_prec = 0;
413 left_pad = 0;
415 else
417 /* Set left_pad to number of spaces needed to align positive
418 and negative formats */
420 int left_bytes = 0;
421 int other_left_bytes = 0;
423 /* Work out number of bytes for currency string and separator
424 preceding the value */
425 if (cs_precedes)
427 left_bytes += strlen (currency_symbol);
428 if (sep_by_space != 0)
429 ++left_bytes;
432 if (other_cs_precedes)
434 other_left_bytes += strlen (currency_symbol);
435 if (other_sep_by_space != 0)
436 ++other_left_bytes;
439 /* Work out number of bytes for the sign (or left parenthesis)
440 preceding the value */
441 if (sign_posn == 0 && is_negative)
442 ++left_bytes;
443 else if (sign_posn == 1)
444 left_bytes += strlen (sign_string);
445 else if (cs_precedes && (sign_posn == 3 || sign_posn == 4))
446 left_bytes += strlen (sign_string);
448 if (other_sign_posn == 0 && !is_negative)
449 ++other_left_bytes;
450 else if (other_sign_posn == 1)
451 other_left_bytes += strlen (other_sign_string);
452 else if (other_cs_precedes &&
453 (other_sign_posn == 3 || other_sign_posn == 4))
454 other_left_bytes += strlen (other_sign_string);
456 /* Compare the number of bytes preceding the value for
457 each format, and set the padding accordingly */
458 if (other_left_bytes > left_bytes)
459 left_pad = other_left_bytes - left_bytes;
460 else
461 left_pad = 0;
464 /* Perhaps we'll someday make these things configurable so
465 better start using symbolic names now. */
466 #define left_paren '('
467 #define right_paren ')'
469 startp = dest; /* Remember start so we can compute length. */
471 while (left_pad-- > 0)
472 out_char (' ');
474 if (sign_posn == 0 && is_negative)
475 out_char (left_paren);
477 if (cs_precedes)
479 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
480 && sign_posn != 5)
482 out_string (sign_string);
483 if (sep_by_space == 2)
484 out_char (' ');
487 if (print_curr_symbol)
489 out_string (currency_symbol);
491 if (sign_posn == 4)
493 if (sep_by_space == 2)
494 out_char (' ');
495 out_string (sign_string);
496 if (sep_by_space == 1)
497 /* POSIX.2 and SUS are not clear on this case, but C99
498 says a space follows the adjacent-symbol-and-sign */
499 out_char (' ');
501 else
502 if (sep_by_space == 1)
503 out_char (' ');
506 else
507 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
508 && sign_posn != 4 && sign_posn != 5)
509 out_string (sign_string);
511 /* Print the number. */
512 #ifdef USE_IN_LIBIO
513 _IO_init ((_IO_FILE *) &f, 0);
514 _IO_JUMPS ((_IO_FILE *) &f) = &_IO_str_jumps;
515 _IO_str_init_static ((_IO_FILE *) &f, dest, (s + maxsize) - dest, dest);
516 #else
517 memset ((void *) &f, 0, sizeof (f));
518 f.__magic = _IOMAGIC;
519 f.__mode.__write = 1;
520 /* The buffer size is one less than MAXLEN
521 so we have space for the null terminator. */
522 f.__bufp = f.__buffer = (char *) dest;
523 f.__bufsize = (s + maxsize) - dest;
524 f.__put_limit = f.__buffer + f.__bufsize;
525 f.__get_limit = f.__buffer;
526 /* After the buffer is full (MAXLEN characters have been written),
527 any more characters written will go to the bit bucket. */
528 f.__room_funcs = __default_room_functions;
529 f.__io_funcs.__write = NULL;
530 f.__seen = 1;
531 #endif
532 /* We clear the last available byte so we can find out whether
533 the numeric representation is too long. */
534 s[maxsize - 1] = '\0';
536 info.prec = right_prec;
537 info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
538 info.spec = 'f';
539 info.is_long_double = is_long_double;
540 info.is_short = 0;
541 info.is_long = 0;
542 info.alt = 0;
543 info.space = 0;
544 info.left = left;
545 info.showsign = 0;
546 info.group = group;
547 info.pad = pad;
548 info.extra = 1; /* This means use values from LC_MONETARY. */
549 info.wide = 0;
551 ptr = &fpnum;
552 done = __printf_fp ((FILE *) &f, &info, &ptr);
553 if (done < 0)
555 va_end (ap);
556 return -1;
559 if (s[maxsize - 1] != '\0')
561 __set_errno (E2BIG);
562 return -1;
565 dest += done;
567 if (!cs_precedes)
569 if (sign_posn == 3)
571 if (sep_by_space == 1)
572 out_char (' ');
573 out_string (sign_string);
576 if (print_curr_symbol)
578 if ((sign_posn == 3 && sep_by_space == 2)
579 || (sign_posn == 4 && sep_by_space == 1)
580 || (sign_posn == 2 && sep_by_space == 1)
581 || (sign_posn == 1 && sep_by_space == 1)
582 || (sign_posn == 0 && sep_by_space == 1))
583 out_char (' ');
584 out_string (currency_symbol);
585 if (sign_posn == 4)
587 if (sep_by_space == 2)
588 out_char (' ');
589 out_string (sign_string);
594 if (sign_posn == 2)
596 if (sep_by_space == 2)
597 out_char (' ');
598 out_string (sign_string);
601 if (sign_posn == 0 && is_negative)
602 out_char (right_paren);
604 /* Now test whether the output width is filled. */
605 if (dest - startp < width)
607 if (left)
608 /* We simply have to fill using spaces. */
610 out_char (' ');
611 while (dest - startp < width);
612 else
614 int dist = width - (dest - startp);
615 char *cp;
616 for (cp = dest - 1; cp >= startp; --cp)
617 cp[dist] = cp[0];
619 dest += dist;
622 startp[--dist] = ' ';
623 while (dist > 0);
628 /* Terminate the string. */
629 *dest = '\0';
631 va_end (ap);
633 return dest - s;