Replace FSF snail mail address with URLs.
[platform/upstream/glibc.git] / stdlib / strfmon_l.c
1 /* Formatting a monetary value according to the given locale.
2    Copyright (C) 1996,1997,2002,2004,2006,2009,2010 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, see
18    <http://www.gnu.org/licenses/>.  */
19
20 #include <ctype.h>
21 #include <errno.h>
22 #include <langinfo.h>
23 #include <locale.h>
24 #include <monetary.h>
25 #include "../libio/libioP.h"
26 #include "../libio/strfile.h"
27 #include <printf.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include "../locale/localeinfo.h"
32
33
34 #define out_char(Ch)                                                          \
35   do {                                                                        \
36     if (dest >= s + maxsize - 1)                                              \
37       {                                                                       \
38         __set_errno (E2BIG);                                                  \
39         va_end (ap);                                                          \
40         return -1;                                                            \
41       }                                                                       \
42     *dest++ = (Ch);                                                           \
43   } while (0)
44
45 #define out_string(String)                                                    \
46   do {                                                                        \
47     const char *_s = (String);                                                \
48     while (*_s)                                                               \
49       out_char (*_s++);                                                       \
50   } while (0)
51
52 #define out_nstring(String, N)                                                \
53   do {                                                                        \
54     int _n = (N);                                                             \
55     const char *_s = (String);                                                \
56     while (_n-- > 0)                                                          \
57       out_char (*_s++);                                                       \
58   } while (0)
59
60 #define to_digit(Ch) ((Ch) - '0')
61
62
63 /* We use this code also for the extended locale handling where the
64    function gets as an additional argument the locale which has to be
65    used.  To access the values we have to redefine the _NL_CURRENT
66    macro.  */
67 #undef _NL_CURRENT
68 #define _NL_CURRENT(category, item) \
69   (current->values[_NL_ITEM_INDEX (item)].string)
70
71 extern int __printf_fp (FILE *, const struct printf_info *,
72                         const void *const *);
73 libc_hidden_proto (__printf_fp)
74 /* This function determines the number of digit groups in the output.
75    The definition is in printf_fp.c.  */
76 extern unsigned int __guess_grouping (unsigned int intdig_max,
77                                       const char *grouping, wchar_t sepchar);
78
79
80 /* We have to overcome some problems with this implementation.  On the
81    one hand the strfmon() function is specified in XPG4 and of course
82    it has to follow this.  But on the other hand POSIX.2 specifies
83    some information in the LC_MONETARY category which should be used,
84    too.  Some of the information contradicts the information which can
85    be specified in format string.  */
86 ssize_t
87 __vstrfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format,
88               va_list ap)
89 {
90   struct __locale_data *current = loc->__locales[LC_MONETARY];
91   _IO_strfile f;
92   struct printf_info info;
93   char *dest;                   /* Pointer so copy the output.  */
94   const char *fmt;              /* Pointer that walks through format.  */
95
96   dest = s;
97   fmt = format;
98
99   /* Loop through the format-string.  */
100   while (*fmt != '\0')
101     {
102       /* The floating-point value to output.  */
103       union
104       {
105         double dbl;
106         __long_double_t ldbl;
107       }
108       fpnum;
109       int int_format;
110       int print_curr_symbol;
111       int left_prec;
112       int left_pad;
113       int right_prec;
114       int group;
115       char pad;
116       int is_long_double;
117       int p_sign_posn;
118       int n_sign_posn;
119       int sign_posn;
120       int other_sign_posn;
121       int left;
122       int is_negative;
123       int sep_by_space;
124       int other_sep_by_space;
125       int cs_precedes;
126       int other_cs_precedes;
127       const char *sign_string;
128       const char *other_sign_string;
129       int done;
130       const char *currency_symbol;
131       size_t currency_symbol_len;
132       long int width;
133       char *startp;
134       const void *ptr;
135       char space_char;
136
137       /* Process all character which do not introduce a format
138          specification.  */
139       if (*fmt != '%')
140         {
141           out_char (*fmt++);
142           continue;
143         }
144
145       /* "%%" means a single '%' character.  */
146       if (fmt[1] == '%')
147         {
148           out_char (*++fmt);
149           ++fmt;
150           continue;
151         }
152
153       /* Defaults for formatting.  */
154       int_format = 0;                   /* Use international curr. symbol */
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.  */
165
166       /* Parse group characters.  */
167       while (1)
168         {
169           switch (*++fmt)
170             {
171             case '=':                   /* Set fill character.  */
172               pad = *++fmt;
173               if (pad == '\0')
174                 {
175                   /* Premature EOS.  */
176                   __set_errno (EINVAL);
177                   return -1;
178                 }
179               continue;
180             case '^':                   /* Don't group digits.  */
181               group = 0;
182               continue;
183             case '+':                   /* Use +/- for sign of number.  */
184               if (n_sign_posn != -1)
185                 {
186                   __set_errno (EINVAL);
187                   return -1;
188                 }
189               p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
190               n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
191               continue;
192             case '(':                   /* Use ( ) for negative sign.  */
193               if (n_sign_posn != -1)
194                 {
195                   __set_errno (EINVAL);
196                   return -1;
197                 }
198               p_sign_posn = 0;
199               n_sign_posn = 0;
200               continue;
201             case '!':                   /* Don't print the currency symbol.  */
202               print_curr_symbol = 0;
203               continue;
204             case '-':                   /* Print left justified.  */
205               left = 1;
206               continue;
207             default:
208               /* Will stop the loop.  */;
209             }
210           break;
211         }
212
213       if (isdigit (*fmt))
214         {
215           /* Parse field width.  */
216           width = to_digit (*fmt);
217
218           while (isdigit (*++fmt))
219             {
220               int val = to_digit (*fmt);
221
222               if (width > LONG_MAX / 10
223                   || (width == LONG_MAX && val > LONG_MAX % 10))
224                 {
225                   __set_errno (E2BIG);
226                   return -1;
227                 }
228
229               width = width * 10 + val;
230             }
231
232           /* If we don't have enough room for the demanded width we
233              can stop now and return an error.  */
234           if (width >= maxsize - (dest - s))
235             {
236               __set_errno (E2BIG);
237               return -1;
238             }
239         }
240
241       /* Recognize left precision.  */
242       if (*fmt == '#')
243         {
244           if (!isdigit (*++fmt))
245             {
246               __set_errno (EINVAL);
247               return -1;
248             }
249           left_prec = to_digit (*fmt);
250
251           while (isdigit (*++fmt))
252             {
253               left_prec *= 10;
254               left_prec += to_digit (*fmt);
255             }
256         }
257
258       /* Recognize right precision.  */
259       if (*fmt == '.')
260         {
261           if (!isdigit (*++fmt))
262             {
263               __set_errno (EINVAL);
264               return -1;
265             }
266           right_prec = to_digit (*fmt);
267
268           while (isdigit (*++fmt))
269             {
270               right_prec *= 10;
271               right_prec += to_digit (*fmt);
272             }
273         }
274
275       /* Handle modifier.  This is an extension.  */
276       if (*fmt == 'L')
277         {
278           ++fmt;
279           if (!__ldbl_is_dbl)
280             is_long_double = 1;
281         }
282
283       /* Handle format specifier.  */
284       char int_symbol[4];
285       switch (*fmt++)
286         {
287         case 'i': {             /* Use international currency symbol.  */
288           const char *int_curr_symbol;
289
290           int_curr_symbol = _NL_CURRENT (LC_MONETARY, INT_CURR_SYMBOL);
291           strncpy(int_symbol, int_curr_symbol, 3);
292           int_symbol[3] = '\0';
293
294           currency_symbol_len = 3;
295           currency_symbol = &int_symbol[0];
296           space_char = int_curr_symbol[3];
297           int_format = 1;
298           break;
299         }
300         case 'n':               /* Use national currency symbol.  */
301           currency_symbol = _NL_CURRENT (LC_MONETARY, CURRENCY_SYMBOL);
302           currency_symbol_len = strlen (currency_symbol);
303           space_char = ' ';
304           int_format = 0;
305           break;
306         default:                /* Any unrecognized format is an error.  */
307           __set_errno (EINVAL);
308           return -1;
309         }
310
311       /* If not specified by the format string now find the values for
312          the format specification.  */
313       if (p_sign_posn == -1)
314         p_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SIGN_POSN : P_SIGN_POSN);
315       if (n_sign_posn == -1)
316         n_sign_posn = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SIGN_POSN : N_SIGN_POSN);
317
318       if (right_prec == -1)
319         {
320           right_prec = *_NL_CURRENT (LC_MONETARY, int_format ? INT_FRAC_DIGITS : FRAC_DIGITS);
321
322           if (right_prec == CHAR_MAX)
323             right_prec = 2;
324         }
325
326       /* If we have to print the digits grouped determine how many
327          extra characters this means.  */
328       if (group && left_prec != -1)
329         left_prec += __guess_grouping (left_prec,
330                                        _NL_CURRENT (LC_MONETARY, MON_GROUPING),
331                                        *_NL_CURRENT (LC_MONETARY,
332                                                      MON_THOUSANDS_SEP));
333
334       /* Now it's time to get the value.  */
335       if (is_long_double == 1)
336         {
337           fpnum.ldbl = va_arg (ap, long double);
338           is_negative = fpnum.ldbl < 0;
339           if (is_negative)
340             fpnum.ldbl = -fpnum.ldbl;
341         }
342       else
343         {
344           fpnum.dbl = va_arg (ap, double);
345           is_negative = fpnum.dbl < 0;
346           if (is_negative)
347             fpnum.dbl = -fpnum.dbl;
348         }
349
350       /* We now know the sign of the value and can determine the format.  */
351       if (is_negative)
352         {
353           sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
354           /* If the locale does not specify a character for the
355              negative sign we use a '-'.  */
356           if (*sign_string == '\0')
357             sign_string = (const char *) "-";
358           cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES);
359           sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE);
360           sign_posn = n_sign_posn;
361
362           other_sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
363           other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES);
364           other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE);
365           other_sign_posn = p_sign_posn;
366         }
367       else
368         {
369           sign_string = _NL_CURRENT (LC_MONETARY, POSITIVE_SIGN);
370           cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_CS_PRECEDES : P_CS_PRECEDES);
371           sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_P_SEP_BY_SPACE : P_SEP_BY_SPACE);
372           sign_posn = p_sign_posn;
373
374           other_sign_string = _NL_CURRENT (LC_MONETARY, NEGATIVE_SIGN);
375           if (*other_sign_string == '\0')
376             other_sign_string = (const char *) "-";
377           other_cs_precedes = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_CS_PRECEDES : N_CS_PRECEDES);
378           other_sep_by_space = *_NL_CURRENT (LC_MONETARY, int_format ? INT_N_SEP_BY_SPACE : N_SEP_BY_SPACE);
379           other_sign_posn = n_sign_posn;
380         }
381
382       /* Set default values for unspecified information.  */
383       if (cs_precedes != 0)
384         cs_precedes = 1;
385       if (other_cs_precedes != 0)
386         other_cs_precedes = 1;
387       if (sep_by_space == CHAR_MAX)
388         sep_by_space = 0;
389       if (other_sep_by_space == CHAR_MAX)
390         other_sep_by_space = 0;
391       if (sign_posn == CHAR_MAX)
392         sign_posn = 1;
393       if (other_sign_posn == CHAR_MAX)
394         other_sign_posn = 1;
395
396       /* Check for degenerate cases */
397       if (sep_by_space == 2)
398         {
399           if (sign_posn == 0 ||
400               (sign_posn == 1 && !cs_precedes) ||
401               (sign_posn == 2 && cs_precedes))
402             /* sign and symbol are not adjacent, so no separator */
403             sep_by_space = 0;
404         }
405       if (other_sep_by_space == 2)
406         {
407           if (other_sign_posn == 0 ||
408               (other_sign_posn == 1 && !other_cs_precedes) ||
409               (other_sign_posn == 2 && other_cs_precedes))
410             /* sign and symbol are not adjacent, so no separator */
411             other_sep_by_space = 0;
412         }
413
414       /* Set the left precision and padding needed for alignment */
415       if (left_prec == -1)
416         {
417           left_prec = 0;
418           left_pad = 0;
419         }
420       else
421         {
422           /* Set left_pad to number of spaces needed to align positive
423              and negative formats */
424
425           int left_bytes = 0;
426           int other_left_bytes = 0;
427
428           /* Work out number of bytes for currency string and separator
429              preceding the value */
430           if (cs_precedes)
431             {
432               left_bytes += currency_symbol_len;
433               if (sep_by_space != 0)
434                 ++left_bytes;
435             }
436
437           if (other_cs_precedes)
438             {
439               other_left_bytes += currency_symbol_len;
440               if (other_sep_by_space != 0)
441                 ++other_left_bytes;
442             }
443
444           /* Work out number of bytes for the sign (or left parenthesis)
445              preceding the value */
446           if (sign_posn == 0 && is_negative)
447             ++left_bytes;
448           else if (sign_posn == 1)
449             left_bytes += strlen (sign_string);
450           else if (cs_precedes && (sign_posn == 3 || sign_posn == 4))
451             left_bytes += strlen (sign_string);
452
453           if (other_sign_posn == 0 && !is_negative)
454             ++other_left_bytes;
455           else if (other_sign_posn == 1)
456             other_left_bytes += strlen (other_sign_string);
457           else if (other_cs_precedes &&
458                    (other_sign_posn == 3 || other_sign_posn == 4))
459             other_left_bytes += strlen (other_sign_string);
460
461           /* Compare the number of bytes preceding the value for
462              each format, and set the padding accordingly */
463           if (other_left_bytes > left_bytes)
464             left_pad = other_left_bytes - left_bytes;
465           else
466             left_pad = 0;
467         }
468
469       /* Perhaps we'll someday make these things configurable so
470          better start using symbolic names now.  */
471 #define left_paren '('
472 #define right_paren ')'
473
474       startp = dest;            /* Remember start so we can compute length.  */
475
476       while (left_pad-- > 0)
477         out_char (' ');
478
479       if (sign_posn == 0 && is_negative)
480         out_char (left_paren);
481
482       if (cs_precedes)
483         {
484           if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
485               && sign_posn != 5)
486             {
487               out_string (sign_string);
488               if (sep_by_space == 2)
489                 out_char (' ');
490             }
491
492           if (print_curr_symbol)
493             out_string (currency_symbol);
494
495           if (sign_posn == 4)
496             {
497               if (print_curr_symbol && sep_by_space == 2)
498                 out_char (space_char);
499               out_string (sign_string);
500               if (sep_by_space == 1)
501                 /* POSIX.2 and SUS are not clear on this case, but C99
502                    says a space follows the adjacent-symbol-and-sign */
503                 out_char (' ');
504             }
505           else
506             if (print_curr_symbol && sep_by_space == 1)
507               out_char (space_char);
508         }
509       else
510         if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
511             && sign_posn != 4 && sign_posn != 5)
512           out_string (sign_string);
513
514       /* Print the number.  */
515 #ifdef _IO_MTSAFE_IO
516       f._sbf._f._lock = NULL;
517 #endif
518       INTUSE(_IO_init) (&f._sbf._f, 0);
519       _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
520       INTUSE(_IO_str_init_static) (&f, dest,
521                                    (s + maxsize) - dest, dest);
522       /* We clear the last available byte so we can find out whether
523          the numeric representation is too long.  */
524       s[maxsize - 1] = '\0';
525
526       memset (&info, '\0', sizeof (info));
527       info.prec = right_prec;
528       info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
529       info.spec = 'f';
530       info.is_long_double = is_long_double;
531       info.group = group;
532       info.pad = pad;
533       info.extra = 1;           /* This means use values from LC_MONETARY.  */
534
535       ptr = &fpnum;
536       done = __printf_fp (&f._sbf._f, &info, &ptr);
537       if (done < 0)
538         return -1;
539
540       if (s[maxsize - 1] != '\0')
541         {
542           __set_errno (E2BIG);
543           return -1;
544         }
545
546       dest += done;
547
548       if (!cs_precedes)
549         {
550           if (sign_posn == 3)
551             {
552               if (sep_by_space == 1)
553                 out_char (' ');
554               out_string (sign_string);
555             }
556
557           if (print_curr_symbol)
558             {
559               if ((sign_posn == 3 && sep_by_space == 2)
560                   || (sign_posn == 4 && sep_by_space == 1)
561                   || (sign_posn == 2 && sep_by_space == 1)
562                   || (sign_posn == 1 && sep_by_space == 1)
563                   || (sign_posn == 0 && sep_by_space == 1))
564                 out_char (space_char);
565               out_nstring (currency_symbol, currency_symbol_len);
566             }
567
568           if (sign_posn == 4)
569             {
570               if (sep_by_space == 2)
571                 out_char (' ');
572               out_string (sign_string);
573             }
574         }
575
576       if (sign_posn == 2)
577         {
578           if (sep_by_space == 2)
579             out_char (' ');
580           out_string (sign_string);
581         }
582
583       if (sign_posn == 0 && is_negative)
584         out_char (right_paren);
585
586       /* Now test whether the output width is filled.  */
587       if (dest - startp < width)
588         {
589           if (left)
590             /* We simply have to fill using spaces.  */
591             do
592               out_char (' ');
593             while (dest - startp < width);
594           else
595             {
596               long int dist = width - (dest - startp);
597               for (char *cp = dest - 1; cp >= startp; --cp)
598                 cp[dist] = cp[0];
599
600               dest += dist;
601
602               do
603                 startp[--dist] = ' ';
604               while (dist > 0);
605             }
606         }
607     }
608
609   /* Terminate the string.  */
610   *dest = '\0';
611
612   return dest - s;
613 }
614
615 ssize_t
616 ___strfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, ...)
617 {
618   va_list ap;
619
620   va_start (ap, format);
621
622   ssize_t res = __vstrfmon_l (s, maxsize, loc, format, ap);
623
624   va_end (ap);
625
626   return res;
627 }
628 ldbl_strong_alias (___strfmon_l, __strfmon_l)
629 ldbl_weak_alias (___strfmon_l, strfmon_l)