Update copyright notices with scripts/update-copyrights.
[jlayton/glibc.git] / stdlib / strfmon_l.c
1 /* Formatting a monetary value according to the given locale.
2    Copyright (C) 1996-2013 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       _IO_init (&f._sbf._f, 0);
519       _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
520       _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
521       /* We clear the last available byte so we can find out whether
522          the numeric representation is too long.  */
523       s[maxsize - 1] = '\0';
524
525       memset (&info, '\0', sizeof (info));
526       info.prec = right_prec;
527       info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
528       info.spec = 'f';
529       info.is_long_double = is_long_double;
530       info.group = group;
531       info.pad = pad;
532       info.extra = 1;           /* This means use values from LC_MONETARY.  */
533
534       ptr = &fpnum;
535       done = __printf_fp (&f._sbf._f, &info, &ptr);
536       if (done < 0)
537         return -1;
538
539       if (s[maxsize - 1] != '\0')
540         {
541           __set_errno (E2BIG);
542           return -1;
543         }
544
545       dest += done;
546
547       if (!cs_precedes)
548         {
549           if (sign_posn == 3)
550             {
551               if (sep_by_space == 1)
552                 out_char (' ');
553               out_string (sign_string);
554             }
555
556           if (print_curr_symbol)
557             {
558               if ((sign_posn == 3 && sep_by_space == 2)
559                   || (sign_posn == 4 && sep_by_space == 1)
560                   || (sign_posn == 2 && sep_by_space == 1)
561                   || (sign_posn == 1 && sep_by_space == 1)
562                   || (sign_posn == 0 && sep_by_space == 1))
563                 out_char (space_char);
564               out_nstring (currency_symbol, currency_symbol_len);
565             }
566
567           if (sign_posn == 4)
568             {
569               if (sep_by_space == 2)
570                 out_char (' ');
571               out_string (sign_string);
572             }
573         }
574
575       if (sign_posn == 2)
576         {
577           if (sep_by_space == 2)
578             out_char (' ');
579           out_string (sign_string);
580         }
581
582       if (sign_posn == 0 && is_negative)
583         out_char (right_paren);
584
585       /* Now test whether the output width is filled.  */
586       if (dest - startp < width)
587         {
588           if (left)
589             /* We simply have to fill using spaces.  */
590             do
591               out_char (' ');
592             while (dest - startp < width);
593           else
594             {
595               long int dist = width - (dest - startp);
596               for (char *cp = dest - 1; cp >= startp; --cp)
597                 cp[dist] = cp[0];
598
599               dest += dist;
600
601               do
602                 startp[--dist] = ' ';
603               while (dist > 0);
604             }
605         }
606     }
607
608   /* Terminate the string.  */
609   *dest = '\0';
610
611   return dest - s;
612 }
613
614 ssize_t
615 ___strfmon_l (char *s, size_t maxsize, __locale_t loc, const char *format, ...)
616 {
617   va_list ap;
618
619   va_start (ap, format);
620
621   ssize_t res = __vstrfmon_l (s, maxsize, loc, format, ap);
622
623   va_end (ap);
624
625   return res;
626 }
627 ldbl_strong_alias (___strfmon_l, __strfmon_l)
628 ldbl_weak_alias (___strfmon_l, strfmon_l)