(root)/
gcc-13.2.0/
libquadmath/
printf/
quadmath-printf.c
       1  /* GCC Quad-Precision Math Library
       2     Copyright (C) 2011 Free Software Foundation, Inc.
       3     Written by Jakub Jelinek  <jakub@redhat.com>
       4  
       5  This file is part of the libquadmath library.
       6  Libquadmath is free software; you can redistribute it and/or
       7  modify it under the terms of the GNU Library General Public
       8  License as published by the Free Software Foundation; either
       9  version 2 of the License, or (at your option) any later version.
      10  
      11  Libquadmath 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  Library General Public License for more details.
      15  
      16  You should have received a copy of the GNU Library General Public
      17  License along with libquadmath; see the file COPYING.LIB.  If
      18  not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
      19  Boston, MA 02110-1301, USA.  */
      20  
      21  #include <config.h>
      22  #include <stdarg.h>
      23  #include <string.h>
      24  #include <stdio.h>
      25  #include "quadmath-printf.h"
      26  
      27  /* Read a simple integer from a string and update the string pointer.
      28     It is assumed that the first character is a digit.  */
      29  static unsigned int
      30  read_int (const char **pstr)
      31  {
      32    unsigned int retval = (unsigned char) **pstr - '0';
      33  
      34    while (isdigit ((unsigned char) *++(*pstr)))
      35      {
      36        retval *= 10;
      37        retval += (unsigned char) **pstr - '0';
      38      }
      39  
      40    return retval;
      41  }
      42  
      43  #define PADSIZE 16
      44  static char const blanks[PADSIZE] =
      45  {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
      46  static char const zeroes[PADSIZE] =
      47  {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
      48  static wchar_t const wblanks[PADSIZE] =
      49  {
      50    L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
      51    L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ')
      52  };
      53  static wchar_t const wzeroes[PADSIZE] =
      54  {
      55    L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'),
      56    L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0')
      57  };
      58  
      59  attribute_hidden size_t
      60  __quadmath_do_pad (struct __quadmath_printf_file *fp, int wide, int c,
      61  		   size_t n)
      62  {
      63    ssize_t i;
      64    char padbuf[PADSIZE];
      65    wchar_t wpadbuf[PADSIZE];
      66    const char *padstr;
      67    size_t w, written = 0;
      68    if (wide)
      69      {
      70        if (c == ' ')
      71  	padstr = (const char *) wblanks;
      72        else if (c == '0')
      73  	padstr = (const char *) wzeroes;
      74        else
      75  	{
      76  	  padstr = (const char *) wpadbuf;
      77  	  for (i = 0; i < PADSIZE; i++)
      78  	    wpadbuf[i] = c;
      79  	}
      80      }
      81    else
      82      {
      83        if (c == ' ')
      84  	padstr = blanks;
      85        else if (c == '0')
      86  	padstr = zeroes;
      87        else
      88  	{
      89  	  padstr = (const char *) padbuf;
      90  	  for (i = 0; i < PADSIZE; i++)
      91  	    padbuf[i] = c;
      92  	}
      93      }
      94    for (i = n; i >= PADSIZE; i -= PADSIZE)
      95      {
      96        w = PUT (fp, (char *) padstr, PADSIZE);
      97        written += w;
      98        if (w != PADSIZE)
      99  	return written;
     100      }
     101    if (i > 0)
     102      {
     103        w = PUT (fp, (char *) padstr, i);
     104        written += w;
     105      }
     106    return written;
     107  }
     108  
     109  /* This is a stripped down version of snprintf, which just handles
     110     a single %eEfFgGaA format entry with Q modifier.  % has to be
     111     the first character of the format string, no $ can be used.  */
     112  int
     113  quadmath_snprintf (char *str, size_t size, const char *format, ...)
     114  {
     115    struct printf_info info;
     116    va_list ap;
     117    __float128 fpnum, *fpnum_addr = &fpnum, **fpnum_addr2 = &fpnum_addr;
     118    struct __quadmath_printf_file qfp;
     119  
     120    if (*format++ != '%')
     121      return -1;
     122  
     123    /* Clear information structure.  */
     124    memset (&info, '\0', sizeof info);
     125    /* info.alt = 0;
     126    info.space = 0;
     127    info.left = 0;
     128    info.showsign = 0;
     129    info.group = 0;
     130    info.i18n = 0;
     131    info.extra = 0; */
     132    info.pad = ' ';
     133    /* info.wide = 0; */
     134  
     135    /* Check for spec modifiers.  */
     136    do
     137      {
     138        switch (*format)
     139  	{
     140  	case ' ':
     141  	  /* Output a space in place of a sign, when there is no sign.  */
     142  	  info.space = 1;
     143  	  continue;
     144  	case '+':
     145  	  /* Always output + or - for numbers.  */
     146  	  info.showsign = 1;
     147  	  continue;
     148  	case '-':
     149  	  /* Left-justify things.  */
     150  	  info.left = 1;
     151  	  continue;
     152  	case '#':
     153  	  /* Use the "alternate form":
     154  	     Hex has 0x or 0X, FP always has a decimal point.  */
     155  	  info.alt = 1;
     156  	  continue;
     157  	case '0':
     158  	  /* Pad with 0s.  */
     159  	  info.pad = '0';
     160  	  continue;
     161  	case '\'':
     162  	  /* Show grouping in numbers if the locale information
     163  	     indicates any.  */
     164  	  info.group = 1;
     165  	  continue;
     166  	case 'I':
     167  	  /* Use the internationalized form of the output.  Currently
     168  	     means to use the `outdigits' of the current locale.  */
     169  	  info.i18n = 1;
     170  	  continue;
     171  	default:
     172  	  break;
     173  	}
     174        break;
     175      }
     176    while (*++format);
     177  
     178    if (info.left)
     179      info.pad = ' ';
     180  
     181    va_start (ap, format);
     182  
     183    /* Get the field width.  */
     184    /* info.width = 0; */
     185    if (*format == '*')
     186      {
     187        /* The field width is given in an argument.
     188  	 A negative field width indicates left justification.  */
     189        ++format;
     190        info.width = va_arg (ap, int);
     191      }
     192    else if (isdigit (*format))
     193      /* Constant width specification.  */
     194      info.width = read_int (&format);
     195  
     196    /* Get the precision.  */
     197    /* -1 means none given; 0 means explicit 0.  */
     198    info.prec = -1;
     199    if (*format == '.')
     200      {
     201        ++format;
     202        if (*format == '*')
     203  	{
     204  	  /* The precision is given in an argument.  */
     205  	  ++format;
     206  
     207  	  info.prec = va_arg (ap, int);
     208  	}
     209        else if (isdigit (*format))
     210  	info.prec = read_int (&format);
     211        else
     212  	/* "%.?" is treated like "%.0?".  */
     213  	info.prec = 0;
     214      }
     215  
     216    /* Check for type modifiers.  */
     217    /* info.is_long_double = 0;
     218    info.is_short = 0;
     219    info.is_long = 0;
     220    info.is_char = 0;
     221    info.user = 0; */
     222  
     223    /* We require Q modifier.  */
     224    if (*format++ != 'Q')
     225      {
     226        va_end (ap);
     227        return -1;
     228      }
     229  
     230    /* Get the format specification.  */
     231    info.spec = (wchar_t) *format++;
     232    if (info.spec == L_('\0') || *format != '\0')
     233      {
     234        va_end (ap);
     235        return -1;
     236      }
     237  
     238    switch (info.spec)
     239      {
     240      case L_('e'):
     241      case L_('E'):
     242      case L_('f'):
     243      case L_('F'):
     244      case L_('g'):
     245      case L_('G'):
     246      case L_('a'):
     247      case L_('A'):
     248        break;
     249      default:
     250        va_end (ap);
     251        return -1;
     252      }
     253  
     254    fpnum = va_arg (ap, __float128);
     255    va_end (ap);
     256  
     257    qfp.fp = NULL;
     258    qfp.str = str;
     259    qfp.size = size ? size - 1 : 0;
     260    qfp.len = 0;
     261    qfp.file_p = 0;
     262  
     263    if (info.spec == L_('a') || info.spec == L_('A'))
     264      __quadmath_printf_fphex (&qfp, &info, (const void *const *)&fpnum_addr2);
     265    else
     266      __quadmath_printf_fp (&qfp, &info, (const void *const *)&fpnum_addr2);
     267  
     268    if (size)
     269      *qfp.str = '\0';
     270  
     271    return qfp.len;
     272  }
     273  
     274  #ifdef HAVE_PRINTF_HOOKS
     275  static int pa_flt128;
     276  int mod_Q attribute_hidden;
     277  
     278  static void
     279  flt128_va (void *mem, va_list *ap)
     280  { 
     281    __float128 d = va_arg (*ap, __float128);
     282    memcpy (mem, &d, sizeof (d));
     283  }
     284  
     285  static int
     286  flt128_ais (const struct printf_info *info, size_t n __attribute__ ((unused)),
     287  	    int *argtype, int *size)
     288  {
     289    if (info->user & mod_Q)
     290      {
     291        argtype[0] = pa_flt128;
     292        size[0] = sizeof (__float128);
     293        return 1;
     294      }
     295  #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 13)
     296    /* Workaround bug in glibc printf hook handling.  */
     297    size[0] = -1;
     298    switch (info->spec)
     299      {
     300      case L_('i'):
     301      case L_('d'):
     302      case L_('u'):
     303      case L_('o'):
     304      case L_('X'):
     305      case L_('x'):
     306  #if __LONG_MAX__ != __LONG_LONG_MAX__
     307        if (info->is_long_double)
     308  	argtype[0] = PA_INT|PA_FLAG_LONG_LONG;
     309        else
     310  #endif
     311        if (info->is_long)
     312  	argtype[0] = PA_INT|PA_FLAG_LONG;
     313        else if (info->is_short)
     314  	argtype[0] = PA_INT|PA_FLAG_SHORT;
     315        else if (info->is_char)
     316  	argtype[0] = PA_CHAR;
     317        else
     318  	argtype[0] = PA_INT;
     319        return 1;
     320      case L_('e'):
     321      case L_('E'):
     322      case L_('f'):
     323      case L_('F'):
     324      case L_('g'):
     325      case L_('G'):
     326      case L_('a'):
     327      case L_('A'):
     328        if (info->is_long_double)
     329  	argtype[0] = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
     330        else
     331  	argtype[0] = PA_DOUBLE;
     332        return 1;
     333      case L_('c'):
     334        argtype[0] = PA_CHAR;
     335        return 1;
     336      case L_('C'):
     337        argtype[0] = PA_WCHAR;
     338        return 1;
     339      case L_('s'):
     340        argtype[0] = PA_STRING;
     341        return 1;
     342      case L_('S'):
     343        argtype[0] = PA_WSTRING;
     344        return 1;
     345      case L_('p'):
     346        argtype[0] = PA_POINTER;
     347        return 1;
     348      case L_('n'):
     349        argtype[0] = PA_INT|PA_FLAG_PTR;
     350        return 1;
     351  
     352      case L_('m'):
     353      default:
     354        /* An unknown spec will consume no args.  */
     355        return 0;
     356      }
     357  #endif
     358    return -1;
     359  }
     360  
     361  static int
     362  flt128_printf_fp (FILE *fp, const struct printf_info *info,
     363  		  const void *const *args)
     364  {
     365    struct __quadmath_printf_file qpf
     366      = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
     367  
     368    if ((info->user & mod_Q) == 0)
     369      return -2;
     370  
     371    return __quadmath_printf_fp (&qpf, info, args);
     372  }
     373  
     374  static int
     375  flt128_printf_fphex (FILE *fp, const struct printf_info *info,
     376  		     const void *const *args)
     377  {
     378    struct __quadmath_printf_file qpf
     379      = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
     380  
     381    if ((info->user & mod_Q) == 0)
     382      return -2;
     383  
     384    return __quadmath_printf_fphex (&qpf, info, args);
     385  }
     386  
     387  __attribute__((constructor)) static void
     388  register_printf_flt128 (void)
     389  {
     390    pa_flt128 = register_printf_type (flt128_va);
     391    if (pa_flt128 == -1)
     392      return;
     393    mod_Q = register_printf_modifier (L_("Q"));
     394    if (mod_Q == -1)
     395      return;
     396    register_printf_specifier ('f', flt128_printf_fp, flt128_ais);
     397    register_printf_specifier ('F', flt128_printf_fp, flt128_ais);
     398    register_printf_specifier ('e', flt128_printf_fp, flt128_ais);
     399    register_printf_specifier ('E', flt128_printf_fp, flt128_ais);
     400    register_printf_specifier ('g', flt128_printf_fp, flt128_ais);
     401    register_printf_specifier ('G', flt128_printf_fp, flt128_ais);
     402    register_printf_specifier ('a', flt128_printf_fphex, flt128_ais);
     403    register_printf_specifier ('A', flt128_printf_fphex, flt128_ais);
     404  }
     405  
     406  __attribute__((destructor)) static void
     407  unregister_printf_flt128 (void)
     408  {
     409    /* No way to unregister printf type and modifier currently,
     410       and only one printf specifier can be registered right now.  */
     411    if (pa_flt128 == -1 || mod_Q == -1)
     412      return;
     413    register_printf_specifier ('f', NULL, NULL);
     414    register_printf_specifier ('F', NULL, NULL);
     415    register_printf_specifier ('e', NULL, NULL);
     416    register_printf_specifier ('E', NULL, NULL);
     417    register_printf_specifier ('g', NULL, NULL);
     418    register_printf_specifier ('G', NULL, NULL);
     419    register_printf_specifier ('a', NULL, NULL);
     420    register_printf_specifier ('A', NULL, NULL);
     421  }
     422  #endif