Update.
[glibc.git] / stdlib / strfmon.c
blobc956ae3040339d88f6ae255a231c6672692afe31
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. */
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 right_prec;
121 int group;
122 char pad;
123 int is_long_double;
124 int p_sign_posn;
125 int n_sign_posn;
126 int sign_posn;
127 int left;
128 int is_negative;
129 int sep_by_space;
130 int cs_precedes;
131 char sign_char;
132 int done;
133 const char *currency_symbol;
134 int width;
135 char *startp;
136 const void *ptr;
138 /* Process all character which do not introduce a format
139 specification. */
140 if (*fmt != '%')
142 out_char (*fmt++);
143 continue;
146 /* "%%" means a single '%' character. */
147 if (fmt[1] == '%')
149 out_char (*++fmt);
150 ++fmt;
151 continue;
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. */
167 while (1)
169 switch (*++fmt)
171 case '=': /* Set fill character. */
172 pad = *++fmt;
173 if (pad == '\0')
175 /* Premature EOS. */
176 __set_errno (EINVAL);
177 va_end (ap);
178 return -1;
180 continue;
181 case '^': /* Don't group digits. */
182 group = 0;
183 continue;
184 case '+': /* Use +/- for sign of number. */
185 if (n_sign_posn != -1)
187 __set_errno (EINVAL);
188 va_end (ap);
189 return -1;
191 if (*_NL_CURRENT (LC_MONETARY, P_SIGN_POSN) == '\0')
192 p_sign_posn = 1;
193 else
194 p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
195 if (*_NL_CURRENT (LC_MONETARY, N_SIGN_POSN) == '\0')
196 n_sign_posn = 1;
197 else
198 n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
199 continue;
200 case '(': /* Use ( ) for negative sign. */
201 if (n_sign_posn != -1)
203 __set_errno (EINVAL);
204 va_end (ap);
205 return -1;
207 n_sign_posn = 5; /* This is a else unused value. */
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 (isdigit (*fmt))
223 /* Parse field width. */
224 width = to_digit (*fmt);
226 while (isdigit (*++fmt))
228 width *= 10;
229 width += to_digit (*fmt);
232 /* If we don't have enough room for the demanded width we
233 can stop now and return an error. */
234 if (dest + width >= s + maxsize)
236 __set_errno (E2BIG);
237 va_end (ap);
238 return -1;
242 /* Recognize left precision. */
243 if (*fmt == '#')
245 if (!isdigit (*++fmt))
247 __set_errno (EINVAL);
248 va_end (ap);
249 return -1;
251 left_prec = to_digit (*fmt);
253 while (isdigit (*++fmt))
255 left_prec *= 10;
256 left_prec += to_digit (*fmt);
260 /* Recognize right precision. */
261 if (*fmt == '.')
263 if (!isdigit (*++fmt))
265 __set_errno (EINVAL);
266 va_end (ap);
267 return -1;
269 right_prec = to_digit (*fmt);
271 while (isdigit (*++fmt))
273 right_prec *= 10;
274 right_prec += to_digit (*fmt);
278 /* Handle modifier. This is an extension. */
279 if (*fmt == 'L')
281 ++fmt;
282 is_long_double = 1;
285 /* Handle format specifier. */
286 switch (*fmt++)
288 case 'i': /* Use international currency symbol. */
289 currency_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL);
290 if (right_prec == -1)
291 if (*_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS) == '\177')
292 right_prec = 2;
293 else
294 right_prec = *_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS);
295 break;
296 case 'n': /* Use national currency symbol. */
297 currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL);
298 if (right_prec == -1)
299 if (*_NL_CURRENT (LC_MONETARY, FRAC_DIGITS) == '\177')
300 right_prec = 2;
301 else
302 right_prec = *_NL_CURRENT (LC_MONETARY, FRAC_DIGITS);
303 break;
304 default: /* Any unrecognized format is an error. */
305 __set_errno (EINVAL);
306 va_end (ap);
307 return -1;
310 /* If we have to print the digits grouped determine how many
311 extra characters this means. */
312 if (group && left_prec != -1)
313 left_prec += __guess_grouping (left_prec,
314 _NL_CURRENT (LC_MONETARY, MON_GROUPING),
315 *_NL_CURRENT (LC_MONETARY,
316 MON_THOUSANDS_SEP));
318 /* Now it's time to get the value. */
319 if (is_long_double == 1)
321 fpnum.ldbl = va_arg (ap, long double);
322 is_negative = fpnum.ldbl < 0;
323 if (is_negative)
324 fpnum.ldbl = -fpnum.ldbl;
326 else
328 fpnum.dbl = va_arg (ap, double);
329 is_negative = fpnum.dbl < 0;
330 if (is_negative)
331 fpnum.dbl = -fpnum.dbl;
334 /* We now know the sign of the value and can determine the format. */
335 if (is_negative)
337 sign_char = *_NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
338 /* If the locale does not specify a character for the
339 negative sign we use a '-'. */
340 if (sign_char == '\0')
341 sign_char = '-';
342 cs_precedes = *_NL_CURRENT (LC_MONETARY, N_CS_PRECEDES);
343 sep_by_space = *_NL_CURRENT (LC_MONETARY, N_SEP_BY_SPACE);
344 /* If the '(' flag is not given use the sign position from
345 the current locale. */
346 if (n_sign_posn == -1)
347 sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
348 else
349 /* This means use parentheses. */
350 sign_posn = 0;
352 else
354 sign_char = *_NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
355 /* If the locale does not specify a character for the
356 positive sign we use a <SP>. */
357 if (sign_char == '\0')
358 sign_char = ' ';
359 cs_precedes = *_NL_CURRENT (LC_MONETARY, P_CS_PRECEDES);
360 sep_by_space = *_NL_CURRENT (LC_MONETARY, P_SEP_BY_SPACE);
361 if (n_sign_posn == -1)
362 sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
363 else
364 /* Here we don't set SIGN_POSN to 0 because we don'want to
365 print <SP> instead of the braces and this is what the
366 value 5 means. */
367 sign_posn = 5;
370 /* Set default values for unspecified information. */
371 if (cs_precedes != 0)
372 cs_precedes = 1;
373 if (sep_by_space == 127)
374 sep_by_space = 0;
375 if (left_prec == -1)
376 left_prec = 0;
379 /* Perhaps we'll someday make these things configurable so
380 better start using symbolic names now. */
381 #define left_paren '('
382 #define right_paren ')'
384 startp = dest; /* Remember start so we can compute length. */
386 if (sign_posn == 0)
387 out_char (left_paren);
388 if (sign_posn == 5) /* This is for positive number and ( flag. */
389 out_char (' ');
391 if (cs_precedes)
393 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
394 && sign_posn != 5)
396 out_char (sign_char);
397 if (sep_by_space == 2)
398 out_char (' ');
401 if (print_curr_symbol)
403 out_string (currency_symbol);
405 if (sign_posn == 4)
407 if (sep_by_space == 2)
408 out_char (' ');
409 out_char (sign_char);
411 else
412 if (sep_by_space == 1)
413 out_char (' ');
416 else
417 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
418 && sign_posn != 4 && sign_posn != 5)
419 out_char (sign_char);
421 /* Print the number. */
422 #ifdef USE_IN_LIBIO
423 _IO_init ((_IO_FILE *) &f, 0);
424 _IO_JUMPS ((_IO_FILE *) &f) = &_IO_str_jumps;
425 _IO_str_init_static ((_IO_FILE *) &f, dest, (s + maxsize) - dest, dest);
426 #else
427 memset((void *) &f, 0, sizeof(f));
428 f.__magic = _IOMAGIC;
429 f.__mode.__write = 1;
430 /* The buffer size is one less than MAXLEN
431 so we have space for the null terminator. */
432 f.__bufp = f.__buffer = (char *) dest;
433 f.__bufsize = (s + maxsize) - dest;
434 f.__put_limit = f.__buffer + f.__bufsize;
435 f.__get_limit = f.__buffer;
436 /* After the buffer is full (MAXLEN characters have been written),
437 any more characters written will go to the bit bucket. */
438 f.__room_funcs = __default_room_functions;
439 f.__io_funcs.__write = NULL;
440 f.__seen = 1;
441 #endif
442 /* We clear the last available byte so we can find out whether
443 the numeric representation is too long. */
444 s[maxsize - 1] = '\0';
446 info.prec = right_prec;
447 info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
448 info.spec = 'f';
449 info.is_long_double = is_long_double;
450 info.is_short = 0;
451 info.is_long = 0;
452 info.alt = 0;
453 info.space = 0;
454 info.left = left;
455 info.showsign = 0;
456 info.group = group;
457 info.pad = pad;
458 info.extra = 1; /* This means use values from LC_MONETARY. */
460 ptr = &fpnum;
461 done = __printf_fp ((FILE *) &f, &info, &ptr);
462 if (done < 0)
464 va_end (ap);
465 return -1;
468 if (s[maxsize - 1] != '\0')
469 return -1;
471 dest += done;
473 if (!cs_precedes)
475 if (sign_posn == 3)
477 if (sep_by_space == 1)
478 out_char (' ');
479 out_char (sign_char);
482 if (print_curr_symbol)
484 if (sign_posn == 3 && sep_by_space == 2)
485 out_char (' ');
486 out_string (currency_symbol);
489 else
490 if (sign_posn == 2)
492 if (sep_by_space == 2)
493 out_char (' ');
494 out_char (sign_char);
497 if (sign_posn == 0)
498 out_char (right_paren);
499 if (sign_posn == 5)
500 out_char (' '); /* This is for positive number and ( flag. */
502 /* Now test whether the output width is filled. */
503 if (dest - startp < width)
504 if (left)
505 /* We simply have to fill using spaces. */
507 out_char (' ');
508 while (dest - startp < width);
509 else
511 int dist = width - (dest - startp);
512 char *cp;
513 for (cp = dest - 1; cp >= startp; --cp)
514 cp[dist] = cp[0];
516 dest += dist;
519 startp[--dist] = ' ';
520 while (dist > 0);
524 /* Terminate the string. */
525 out_char ('\0');
527 va_end (ap);
529 return dest - s - 1;