Update to 2.1.x development version
[glibc/history.git] / stdlib / strfmon.c
blob54771049ad25de2c5e8b804d4db9824db1540e9a
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 continue;
174 case '^': /* Don't group digits. */
175 group = 0;
176 continue;
177 case '+': /* Use +/- for sign of number. */
178 if (n_sign_posn != -1)
180 __set_errno (EINVAL);
181 va_end (ap);
182 return -1;
184 if (*_NL_CURRENT (LC_MONETARY, P_SIGN_POSN) == '\0')
185 p_sign_posn = 1;
186 else
187 p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
188 if (*_NL_CURRENT (LC_MONETARY, N_SIGN_POSN) == '\0')
189 n_sign_posn = 1;
190 else
191 n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
192 continue;
193 case '(': /* Use ( ) for negative sign. */
194 if (n_sign_posn != -1)
196 __set_errno (EINVAL);
197 va_end (ap);
198 return -1;
200 n_sign_posn = 5; /* This is a else unused value. */
201 continue;
202 case '!': /* Don't print the currency symbol. */
203 print_curr_symbol = 0;
204 continue;
205 case '-': /* Print left justified. */
206 left = 1;
207 continue;
208 default:
209 /* Will stop the loop. */;
211 break;
214 if (isdigit (*fmt))
216 /* Parse field width. */
217 width = to_digit (*fmt);
219 while (isdigit (*++fmt))
221 width *= 10;
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)
229 __set_errno (E2BIG);
230 va_end (ap);
231 return -1;
235 /* Recognize left precision. */
236 if (*fmt == '#')
238 if (!isdigit (*++fmt))
240 __set_errno (EINVAL);
241 va_end (ap);
242 return -1;
244 left_prec = to_digit (*fmt);
246 while (isdigit (*++fmt))
248 left_prec *= 10;
249 left_prec += to_digit (*fmt);
253 /* Recognize right precision. */
254 if (*fmt == '.')
256 if (!isdigit (*++fmt))
258 __set_errno (EINVAL);
259 va_end (ap);
260 return -1;
262 right_prec = to_digit (*fmt);
264 while (isdigit (*++fmt))
266 right_prec *= 10;
267 right_prec += to_digit (*fmt);
271 /* Handle modifier. This is an extension. */
272 if (*fmt == 'L')
274 ++fmt;
275 is_long_double = 1;
278 /* Handle format specifier. */
279 switch (*fmt++)
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')
285 right_prec = 2;
286 else
287 right_prec = *_NL_CURRENT (LC_MONETARY, INT_FRAC_DIGITS);
288 break;
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')
293 right_prec = 2;
294 else
295 right_prec = *_NL_CURRENT (LC_MONETARY, FRAC_DIGITS);
296 break;
297 default: /* Any unrecognized format is an error. */
298 __set_errno (EINVAL);
299 va_end (ap);
300 return -1;
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,
309 MON_THOUSANDS_SEP));
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;
316 if (is_negative)
317 fpnum.ldbl = -fpnum.ldbl;
319 else
321 fpnum.dbl = va_arg (ap, double);
322 is_negative = fpnum.dbl < 0;
323 if (is_negative)
324 fpnum.dbl = -fpnum.dbl;
327 /* We now know the sign of the value and can determine the format. */
328 if (is_negative)
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')
334 sign_char = '-';
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);
341 else
342 /* This means use parentheses. */
343 sign_posn = 0;
345 else
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')
351 sign_char = ' ';
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);
356 else
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
359 value 5 means. */
360 sign_posn = 5;
363 /* Set default values for unspecified information. */
364 if (cs_precedes != 0)
365 cs_precedes = 1;
366 if (sep_by_space == 127)
367 sep_by_space = 0;
368 if (left_prec == -1)
369 left_prec = 0;
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. */
379 if (sign_posn == 0)
380 out_char (left_paren);
381 if (sign_posn == 5) /* This is for positive number and ( flag. */
382 out_char (' ');
384 if (cs_precedes)
386 if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
387 && sign_posn != 5)
389 out_char (sign_char);
390 if (sep_by_space == 2)
391 out_char (' ');
394 if (print_curr_symbol)
396 out_string (currency_symbol);
398 if (sign_posn == 4)
400 if (sep_by_space == 2)
401 out_char (' ');
402 out_char (sign_char);
404 else
405 if (sep_by_space == 1)
406 out_char (' ');
409 else
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. */
415 #ifdef USE_IN_LIBIO
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);
419 #else
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;
433 f.__seen = 1;
434 #endif
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);
441 info.spec = 'f';
442 info.is_long_double = is_long_double;
443 info.is_short = 0;
444 info.is_long = 0;
445 info.alt = 0;
446 info.space = 0;
447 info.left = left;
448 info.showsign = 0;
449 info.group = group;
450 info.pad = pad;
451 info.extra = 1; /* This means use values from LC_MONETARY. */
453 ptr = &fpnum;
454 done = __printf_fp ((FILE *) &f, &info, &ptr);
455 if (done < 0)
457 va_end (ap);
458 return -1;
461 if (s[maxsize - 1] != '\0')
462 return -1;
464 dest += done;
466 if (!cs_precedes)
468 if (sign_posn == 3)
470 if (sep_by_space == 1)
471 out_char (' ');
472 out_char (sign_char);
475 if (print_curr_symbol)
477 if (sign_posn == 3 && sep_by_space == 2)
478 out_char (' ');
479 out_string (currency_symbol);
482 else
483 if (sign_posn == 2)
485 if (sep_by_space == 2)
486 out_char (' ');
487 out_char (sign_char);
490 if (sign_posn == 0)
491 out_char (right_paren);
492 if (sign_posn == 5)
493 out_char (' '); /* This is for positive number and ( flag. */
495 /* Now test whether the output width is filled. */
496 if (dest - startp < width)
497 if (left)
498 /* We simply have to fill using spaces. */
500 out_char (' ');
501 while (dest - startp < width);
502 else
504 int dist = width - (dest - startp);
505 char *cp;
506 for (cp = dest - 1; cp >= startp; --cp)
507 cp[dist] = cp[0];
509 dest += dist;
512 startp[--dist] = ' ';
513 while (dist > 0);
517 /* Terminate the string. */
518 out_char ('\0');
520 va_end (ap);
522 return dest - s - 1;