root/maint/gnulib/lib/strfmon_l.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fmon_parse
  2. rpl_strfmon_l

   1 /* strfmon_l override.
   2    Copyright (C) 2017-2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 2.1 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 #include <config.h>
  18 
  19 /* Specification.  */
  20 #include <monetary.h>
  21 
  22 #include <errno.h>
  23 #include <locale.h>
  24 #include <stdarg.h>
  25 #include <stdbool.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 #undef strfmon_l
  30 
  31 /* This override can only support a limited number of arguments.  */
  32 #define MAX_ARGS 16
  33 
  34 /* A parsed directive.  */
  35 typedef struct
  36 {
  37   bool needs_long_double;
  38   const char *conversion_ptr;
  39 }
  40 directive_t;
  41 
  42 /* A parsed format string.  */
  43 typedef struct
  44 {
  45   size_t count;
  46   directive_t dir[MAX_ARGS];
  47 }
  48 directives_t;
  49 
  50 /* Parses a monetary format string.
  51    Returns 0 and fills *DIRECTIVESP if valid.
  52    Returns -1 if invalid.  */
  53 static int
  54 fmon_parse (const char *format, directives_t *directivesp)
     /* [previous][next][first][last][top][bottom][index][help] */
  55 {
  56   size_t count = 0;
  57   const char *cp = format;
  58 
  59   while (*cp != '\0')
  60     {
  61       if (*cp++ == '%')
  62         {
  63           /* Parse flags.  */
  64           while (*cp == '=' || *cp == '^' || *cp == '+' || *cp == '('
  65                  || *cp == '!' || *cp == '-')
  66             {
  67               if (*cp == '=')
  68                 {
  69                   cp++;
  70                   if (*cp == '\0')
  71                     return -1;
  72                 }
  73               cp++;
  74             }
  75           /* Parse field width.  */
  76           while (*cp >= '0' && *cp <= '9')
  77             cp++;
  78           /* Parse left precision.  */
  79           if (*cp == '#')
  80             {
  81               cp++;
  82               while (*cp >= '0' && *cp <= '9')
  83                 cp++;
  84             }
  85           /* Parse right precision.  */
  86           if (*cp == '.')
  87             {
  88               cp++;
  89               while (*cp >= '0' && *cp <= '9')
  90                 cp++;
  91             }
  92           /* Now comes the conversion specifier.  */
  93           if (*cp != '%')
  94             {
  95               if (count == MAX_ARGS)
  96                 /* Too many arguments.  */
  97                 return -1;
  98 
  99               /* glibc supports an 'L' modifier before the conversion specifier.  */
 100               if (*cp == 'L')
 101                 {
 102                   cp++;
 103                   directivesp->dir[count].needs_long_double = true;
 104                 }
 105               else
 106                 directivesp->dir[count].needs_long_double = false;
 107               if (!(*cp == 'i' || *cp == 'n'))
 108                 return -1;
 109               directivesp->dir[count].conversion_ptr = cp;
 110               count++;
 111             }
 112           cp++;
 113         }
 114     }
 115 
 116   directivesp->count = count;
 117   return 0;
 118 }
 119 
 120 ssize_t
 121 rpl_strfmon_l (char *s, size_t maxsize, locale_t locale, const char *format, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 122 {
 123   /* Work around glibc 2.23 bug
 124      <https://sourceware.org/bugzilla/show_bug.cgi?id=19633>.  */
 125   va_list argptr;
 126   locale_t orig_locale;
 127   directives_t directives;
 128   ssize_t result;
 129 
 130   orig_locale = uselocale ((locale_t)0);
 131 
 132   if (uselocale (locale) == (locale_t)0)
 133     /* errno is set.  */
 134     return -1;
 135 
 136   /* The format string may consume 'double' or 'long double' arguments.
 137      In order not to have to link with libffcall or libffi, convert all
 138      arguments to 'long double', and use a modified format string that
 139      requests 'long double' arguments.  But since 'long double' arguments
 140      are only supported on glibc, do so only if the original format string
 141      consumes at least one 'long double' argument.  */
 142   if (fmon_parse (format, &directives) < 0)
 143     {
 144       errno = EINVAL;
 145       result = -1;
 146     }
 147   else
 148     {
 149       bool use_long_double;
 150       unsigned int i;
 151 
 152       use_long_double = false;
 153       for (i = 0; i < directives.count; i++)
 154         if (directives.dir[i].needs_long_double)
 155           {
 156             use_long_double = true;
 157             break;
 158           }
 159 
 160       va_start (argptr, format);
 161 
 162       if (use_long_double)
 163         {
 164           char *ld_format;
 165 
 166           /* Allocate room for the modified format string.  */
 167           ld_format = (char *) malloc (strlen (format) + directives.count + 1);
 168           if (ld_format == NULL)
 169             {
 170               errno = ENOMEM;
 171               result = -1;
 172             }
 173           else
 174             {
 175               long double args[MAX_ARGS];
 176 
 177               /* Create the modified format string.  */
 178               {
 179                 const char *p = format;
 180                 char *dest = ld_format;
 181                 for (i = 0; i < directives.count; i++)
 182                   {
 183                     const char *q = directives.dir[i].conversion_ptr;
 184                     memcpy (dest, p, q - p);
 185                     dest += q - p;
 186                     if (!directives.dir[i].needs_long_double)
 187                       *dest++ = 'L';
 188                     p = q;
 189                   }
 190                 strcpy (dest, p);
 191               }
 192 
 193               /* Set up arguments array.  */
 194               for (i = 0; i < directives.count; i++)
 195                 args[i] = (directives.dir[i].needs_long_double
 196                            ? va_arg (argptr, long double)
 197                            : (long double) va_arg (argptr, double));
 198               /* Avoid uninitialized memory references.  */
 199               for (; i < MAX_ARGS; i++)
 200                 args[i] = 0.0L;
 201 
 202               result = strfmon_l (s, maxsize, locale, ld_format,
 203                                   args[0], args[1], args[2], args[3], args[4],
 204                                   args[5], args[6], args[7], args[8], args[9],
 205                                   args[10], args[11], args[12], args[13],
 206                                   args[14], args[15]);
 207 
 208               free (ld_format);
 209             }
 210         }
 211       else
 212         {
 213           double args[MAX_ARGS];
 214 
 215           /* Set up arguments array.  */
 216           for (i = 0; i < directives.count; i++)
 217             args[i] = va_arg (argptr, double);
 218           /* Avoid uninitialized memory references.  */
 219           for (; i < MAX_ARGS; i++)
 220             args[i] = 0.0;
 221 
 222           result = strfmon_l (s, maxsize, locale, format,
 223                               args[0], args[1], args[2], args[3], args[4],
 224                               args[5], args[6], args[7], args[8], args[9],
 225                               args[10], args[11], args[12], args[13], args[14],
 226                               args[15]);
 227         }
 228 
 229       va_end (argptr);
 230     }
 231 
 232   if (uselocale (orig_locale) == (locale_t)0)
 233     /* errno is set.  */
 234     return -1;
 235 
 236   return result;
 237 }

/* [previous][next][first][last][top][bottom][index][help] */