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