Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/port/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 36 kB image not shown  

SSL snprintf.c

  Interaktion und
PortierbarkeitC
 

/*
 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
 * Copyright (c) 1988, 1993
 * The Regents of the University of California.  All rights reserved.
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * src/port/snprintf.c
 */


#include "c.h"

#include <math.h>

/*
 * We used to use the platform's NL_ARGMAX here, but that's a bad idea,
 * first because the point of this module is to remove platform dependencies
 * not perpetuate them, and second because some platforms use ridiculously
 * large values, leading to excessive stack consumption in dopr().
 */

#define PG_NL_ARGMAX 31


/*
 * SNPRINTF, VSNPRINTF and friends
 *
 * These versions have been grabbed off the net.  They have been
 * cleaned up to compile properly and support for most of the C99
 * specification has been added.  Remaining unimplemented features are:
 *
 * 1. No locale support: the radix character is always '.' and the '
 * (single quote) format flag is ignored.
 *
 * 2. No support for the "%n" format specification.
 *
 * 3. No support for wide characters ("lc" and "ls" formats).
 *
 * 4. No support for "long double" ("Lf" and related formats).
 *
 * 5. Space and '#' flags are not implemented.
 *
 * In addition, we support some extensions over C99:
 *
 * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
 *
 * 2. "%m" expands to the value of strerror(errno), where errno is the
 * value that variable had at the start of the call.  This is a glibc
 * extension, but a very useful one.
 *
 *
 * Historically the result values of sprintf/snprintf varied across platforms.
 * This implementation now follows the C99 standard:
 *
 * 1. -1 is returned if an error is detected in the format string, or if
 * a write to the target stream fails (as reported by fwrite).  Note that
 * overrunning snprintf's target buffer is *not* an error.
 *
 * 2. For successful writes to streams, the actual number of bytes written
 * to the stream is returned.
 *
 * 3. For successful sprintf/snprintf, the number of bytes that would have
 * been written to an infinite-size buffer (excluding the trailing '\0')
 * is returned.  snprintf will truncate its output to fit in the buffer
 * (ensuring a trailing '\0' unless count == 0), but this is not reflected
 * in the function result.
 *
 * snprintf buffer overrun can be detected by checking for function result
 * greater than or equal to the supplied count.
 */


/**************************************************************
 * Original:
 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
 * A bombproof version of doprnt (dopr) included.
 * Sigh.  This sort of thing is always nasty do deal with.  Note that
 * the version here does not include floating point. (now it does ... tgl)
 **************************************************************/


/* Prevent recursion */
#undef vsnprintf
#undef snprintf
#undef vsprintf
#undef sprintf
#undef vfprintf
#undef fprintf
#undef vprintf
#undef printf

/*
 * Info about where the formatted output is going.
 *
 * dopr and subroutines will not write at/past bufend, but snprintf
 * reserves one byte, ensuring it may place the trailing '\0' there.
 *
 * In snprintf, we use nchars to count the number of bytes dropped on the
 * floor due to buffer overrun.  The correct result of snprintf is thus
 * (bufptr - bufstart) + nchars.  (This isn't as inconsistent as it might
 * seem: nchars is the number of emitted bytes that are not in the buffer now,
 * either because we sent them to the stream or because we couldn't fit them
 * into the buffer to begin with.)
 */

typedef struct
{
 char    *bufptr;   /* next buffer output position */
 char    *bufstart;  /* first buffer element */
 char    *bufend;   /* last+1 buffer element, or NULL */
 /* bufend == NULL is for sprintf, where we assume buf is big enough */
 FILE    *stream;   /* eventual output destination, or NULL */
 int   nchars;   /* # chars sent to stream, or dropped */
 bool  failed;   /* call is a failure; errno is set */
} PrintfTarget;

/*
 * Info about the type and value of a formatting parameter.  Note that we
 * don't currently support "long double", "wint_t", or "wchar_t *" data,
 * nor the '%n' formatting code; else we'd need more types.  Also, at this
 * level we need not worry about signed vs unsigned values.
 */

typedef enum
{
 ATYPE_NONE = 0,
 ATYPE_INT,
 ATYPE_LONG,
 ATYPE_LONGLONG,
 ATYPE_DOUBLE,
 ATYPE_CHARPTR
} PrintfArgType;

typedef union
{
 int   i;
 long  l;
 long long ll;
 double  d;
 char    *cptr;
} PrintfArgValue;


static void flushbuffer(PrintfTarget *target);
static void dopr(PrintfTarget *target, const char *format, va_list args);


/*
 * Externally visible entry points.
 *
 * All of these are just wrappers around dopr().  Note it's essential that
 * they not change the value of "errno" before reaching dopr().
 */


int
pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
 PrintfTarget target;
 char  onebyte[1];

 /*
  * C99 allows the case str == NULL when count == 0.  Rather than
  * special-casing this situation further down, we substitute a one-byte
  * local buffer.  Callers cannot tell, since the function result doesn't
  * depend on count.
 */

 if (count == 0)
 {
  str = onebyte;
  count = 1;
 }
 target.bufstart = target.bufptr = str;
 target.bufend = str + count - 1;
 target.stream = NULL;
 target.nchars = 0;
 target.failed = false;
 dopr(&target, fmt, args);
 *(target.bufptr) = '\0';
 return target.failed ? -1 : (target.bufptr - target.bufstart
         + target.nchars);
}

int
pg_snprintf(char *str, size_t count, const char *fmt,...)
{
 int   len;
 va_list  args;

 va_start(args, fmt);
 len = pg_vsnprintf(str, count, fmt, args);
 va_end(args);
 return len;
}

int
pg_vsprintf(char *str, const char *fmt, va_list args)
{
 PrintfTarget target;

 target.bufstart = target.bufptr = str;
 target.bufend = NULL;
 target.stream = NULL;
 target.nchars = 0;   /* not really used in this case */
 target.failed = false;
 dopr(&target, fmt, args);
 *(target.bufptr) = '\0';
 return target.failed ? -1 : (target.bufptr - target.bufstart
         + target.nchars);
}

int
pg_sprintf(char *str, const char *fmt,...)
{
 int   len;
 va_list  args;

 va_start(args, fmt);
 len = pg_vsprintf(str, fmt, args);
 va_end(args);
 return len;
}

int
pg_vfprintf(FILE *stream, const char *fmt, va_list args)
{
 PrintfTarget target;
 char  buffer[1024]; /* size is arbitrary */

 if (stream == NULL)
 {
  errno = EINVAL;
  return -1;
 }
 target.bufstart = target.bufptr = buffer;
 target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
 target.stream = stream;
 target.nchars = 0;
 target.failed = false;
 dopr(&target, fmt, args);
 /* dump any remaining buffer contents */
 flushbuffer(&target);
 return target.failed ? -1 : target.nchars;
}

int
pg_fprintf(FILE *stream, const char *fmt,...)
{
 int   len;
 va_list  args;

 va_start(args, fmt);
 len = pg_vfprintf(stream, fmt, args);
 va_end(args);
 return len;
}

int
pg_vprintf(const char *fmt, va_list args)
{
 return pg_vfprintf(stdout, fmt, args);
}

int
pg_printf(const char *fmt,...)
{
 int   len;
 va_list  args;

 va_start(args, fmt);
 len = pg_vfprintf(stdout, fmt, args);
 va_end(args);
 return len;
}

/*
 * Attempt to write the entire buffer to target->stream; discard the entire
 * buffer in any case.  Call this only when target->stream is defined.
 */

static void
flushbuffer(PrintfTarget *target)
{
 size_t  nc = target->bufptr - target->bufstart;

 /*
  * Don't write anything if we already failed; this is to ensure we
  * preserve the original failure's errno.
 */

 if (!target->failed && nc > 0)
 {
  size_t  written;

  written = fwrite(target->bufstart, 1, nc, target->stream);
  target->nchars += written;
  if (written != nc)
   target->failed = true;
 }
 target->bufptr = target->bufstart;
}


static bool find_arguments(const char *format, va_list args,
         PrintfArgValue *argvalues);
static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
       int pointflag, PrintfTarget *target);
static void fmtptr(const void *value, PrintfTarget *target);
static void fmtint(long long value, char type, int forcesign,
       int leftjust, int minlen, int zpad, int precision, int pointflag,
       PrintfTarget *target);
static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
static void fmtfloat(double value, char type, int forcesign,
      int leftjust, int minlen, int zpad, int precision, int pointflag,
      PrintfTarget *target);
static void dostr(const char *str, int slen, PrintfTarget *target);
static void dopr_outch(int c, PrintfTarget *target);
static void dopr_outchmulti(int c, int slen, PrintfTarget *target);
static int adjust_sign(int is_negative, int forcesign, int *signvalue);
static int compute_padlen(int minlen, int vallen, int leftjust);
static void leading_pad(int zpad, int signvalue, int *padlen,
      PrintfTarget *target);
static void trailing_pad(int padlen, PrintfTarget *target);

/*
 * If strchrnul exists (it's a glibc-ism, but since adopted by some other
 * platforms), it's a good bit faster than the equivalent manual loop.
 * Use it if possible, and if it doesn't exist, use this replacement.
 *
 * Note: glibc declares this as returning "char *", but that would require
 * casting away const internally, so we don't follow that detail.
 *
 * Note: macOS has this too as of Sequoia 15.4, but it's hidden behind
 * a deployment-target check that causes compile errors if the deployment
 * target isn't high enough.  So !HAVE_DECL_STRCHRNUL may mean "yes it's
 * declared, but it doesn't compile".  To avoid failing in that scenario,
 * use a macro to avoid matching <string.h>'s name.
 */

#if !HAVE_DECL_STRCHRNUL

#define strchrnul pg_strchrnul

static inline const char *
strchrnul(const char *s, int c)
{
 while (*s != '\0' && *s != c)
  s++;
 return s;
}

#endif       /* !HAVE_DECL_STRCHRNUL */


/*
 * dopr(): the guts of *printf for all cases.
 */

static void
dopr(PrintfTarget *target, const char *format, va_list args)
{
 int   save_errno = errno;
 const char *first_pct = NULL;
 int   ch;
 bool  have_dollar;
 bool  have_star;
 bool  afterstar;
 int   accum;
 int   longlongflag;
 int   longflag;
 int   pointflag;
 int   leftjust;
 int   fieldwidth;
 int   precision;
 int   zpad;
 int   forcesign;
 int   fmtpos;
 int   cvalue;
 long long numvalue;
 double  fvalue;
 const char *strvalue;
 PrintfArgValue argvalues[PG_NL_ARGMAX + 1];

 /*
  * Initially, we suppose the format string does not use %n$.  The first
  * time we come to a conversion spec that has that, we'll call
  * find_arguments() to check for consistent use of %n$ and fill the
  * argvalues array with the argument values in the correct order.
 */

 have_dollar = false;

 while (*format != '\0')
 {
  /* Locate next conversion specifier */
  if (*format != '%')
  {
   /* Scan to next '%' or end of string */
   const char *next_pct = strchrnul(format + 1'%');

   /* Dump literal data we just scanned over */
   dostr(format, next_pct - format, target);
   if (target->failed)
    break;

   if (*next_pct == '\0')
    break;
   format = next_pct;
  }

  /*
   * Remember start of first conversion spec; if we find %n$, then it's
   * sufficient for find_arguments() to start here, without rescanning
   * earlier literal text.
 */

  if (first_pct == NULL)
   first_pct = format;

  /* Process conversion spec starting at *format */
  format++;

  /* Fast path for conversion spec that is exactly %s */
  if (*format == 's')
  {
   format++;
   strvalue = va_arg(args, char *);
   if (strvalue == NULL)
    strvalue = "(null)";
   dostr(strvalue, strlen(strvalue), target);
   if (target->failed)
    break;
   continue;
  }

  fieldwidth = precision = zpad = leftjust = forcesign = 0;
  longflag = longlongflag = pointflag = 0;
  fmtpos = accum = 0;
  have_star = afterstar = false;
nextch2:
  ch = *format++;
  switch (ch)
  {
   case '-':
    leftjust = 1;
    goto nextch2;
   case '+':
    forcesign = 1;
    goto nextch2;
   case '0':
    /* set zero padding if no nonzero digits yet */
    if (accum == 0 && !pointflag)
     zpad = '0';
    /* FALL THRU */
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
    accum = accum * 10 + (ch - '0');
    goto nextch2;
   case '.':
    if (have_star)
     have_star = false;
    else
     fieldwidth = accum;
    pointflag = 1;
    accum = 0;
    goto nextch2;
   case '*':
    if (have_dollar)
    {
     /*
      * We'll process value after reading n$.  Note it's OK to
      * assume have_dollar is set correctly, because in a valid
      * format string the initial % must have had n$ if * does.
 */

     afterstar = true;
    }
    else
    {
     /* fetch and process value now */
     int   starval = va_arg(args, int);

     if (pointflag)
     {
      precision = starval;
      if (precision < 0)
      {
       precision = 0;
       pointflag = 0;
      }
     }
     else
     {
      fieldwidth = starval;
      if (fieldwidth < 0)
      {
       leftjust = 1;
       fieldwidth = -fieldwidth;
      }
     }
    }
    have_star = true;
    accum = 0;
    goto nextch2;
   case '$':
    /* First dollar sign? */
    if (!have_dollar)
    {
     /* Yup, so examine all conversion specs in format */
     if (!find_arguments(first_pct, args, argvalues))
      goto bad_format;
     have_dollar = true;
    }
    if (afterstar)
    {
     /* fetch and process star value */
     int   starval = argvalues[accum].i;

     if (pointflag)
     {
      precision = starval;
      if (precision < 0)
      {
       precision = 0;
       pointflag = 0;
      }
     }
     else
     {
      fieldwidth = starval;
      if (fieldwidth < 0)
      {
       leftjust = 1;
       fieldwidth = -fieldwidth;
      }
     }
     afterstar = false;
    }
    else
     fmtpos = accum;
    accum = 0;
    goto nextch2;
#ifdef WIN32
   case 'I':
    /* Windows PRI*{32,64,PTR} size */
    if (format[0] == '3' && format[1] == '2')
     format += 2;
    else if (format[0] == '6' && format[1] == '4')
    {
     format += 2;
     longlongflag = 1;
    }
    else
    {
#if SIZEOF_VOID_P == SIZEOF_LONG
     longflag = 1;
#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
     longlongflag = 1;
#else
#error "cannot find integer type of the same size as intptr_t"
#endif
    }
    goto nextch2;
#endif
   case 'l':
    if (longflag)
     longlongflag = 1;
    else
     longflag = 1;
    goto nextch2;
   case 'z':
#if SIZEOF_SIZE_T == SIZEOF_LONG
    longflag = 1;
#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
    longlongflag = 1;
#else
#error "cannot find integer type of the same size as size_t"
#endif
    goto nextch2;
   case 'h':
   case '\'':
    /* ignore these */
    goto nextch2;
   case 'd':
   case 'i':
    if (!have_star)
    {
     if (pointflag)
      precision = accum;
     else
      fieldwidth = accum;
    }
    if (have_dollar)
    {
     if (longlongflag)
      numvalue = argvalues[fmtpos].ll;
     else if (longflag)
      numvalue = argvalues[fmtpos].l;
     else
      numvalue = argvalues[fmtpos].i;
    }
    else
    {
     if (longlongflag)
      numvalue = va_arg(args, long long);
     else if (longflag)
      numvalue = va_arg(args, long);
     else
      numvalue = va_arg(args, int);
    }
    fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
        precision, pointflag, target);
    break;
   case 'o':
   case 'u':
   case 'x':
   case 'X':
    if (!have_star)
    {
     if (pointflag)
      precision = accum;
     else
      fieldwidth = accum;
    }
    if (have_dollar)
    {
     if (longlongflag)
      numvalue = (unsigned long long) argvalues[fmtpos].ll;
     else if (longflag)
      numvalue = (unsigned long) argvalues[fmtpos].l;
     else
      numvalue = (unsigned int) argvalues[fmtpos].i;
    }
    else
    {
     if (longlongflag)
      numvalue = (unsigned long long) va_arg(args, long long);
     else if (longflag)
      numvalue = (unsigned long) va_arg(args, long);
     else
      numvalue = (unsigned int) va_arg(args, int);
    }
    fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
        precision, pointflag, target);
    break;
   case 'c':
    if (!have_star)
    {
     if (pointflag)
      precision = accum;
     else
      fieldwidth = accum;
    }
    if (have_dollar)
     cvalue = (unsigned char) argvalues[fmtpos].i;
    else
     cvalue = (unsigned char) va_arg(args, int);
    fmtchar(cvalue, leftjust, fieldwidth, target);
    break;
   case 's':
    if (!have_star)
    {
     if (pointflag)
      precision = accum;
     else
      fieldwidth = accum;
    }
    if (have_dollar)
     strvalue = argvalues[fmtpos].cptr;
    else
     strvalue = va_arg(args, char *);
    /* If string is NULL, silently substitute "(null)" */
    if (strvalue == NULL)
     strvalue = "(null)";
    fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
        target);
    break;
   case 'p':
    /* fieldwidth/leftjust are ignored ... */
    if (have_dollar)
     strvalue = argvalues[fmtpos].cptr;
    else
     strvalue = va_arg(args, char *);
    fmtptr((const void *) strvalue, target);
    break;
   case 'e':
   case 'E':
   case 'f':
   case 'g':
   case 'G':
    if (!have_star)
    {
     if (pointflag)
      precision = accum;
     else
      fieldwidth = accum;
    }
    if (have_dollar)
     fvalue = argvalues[fmtpos].d;
    else
     fvalue = va_arg(args, double);
    fmtfloat(fvalue, ch, forcesign, leftjust,
       fieldwidth, zpad,
       precision, pointflag,
       target);
    break;
   case 'm':
    {
     char  errbuf[PG_STRERROR_R_BUFLEN];
     const char *errm = strerror_r(save_errno,
              errbuf, sizeof(errbuf));

     dostr(errm, strlen(errm), target);
    }
    break;
   case '%':
    dopr_outch('%', target);
    break;
   default:

    /*
     * Anything else --- in particular, '\0' indicating end of
     * format string --- is bogus.
 */

    goto bad_format;
  }

  /* Check for failure after each conversion spec */
  if (target->failed)
   break;
 }

 return;

bad_format:
 errno = EINVAL;
 target->failed = true;
}

/*
 * find_arguments(): sort out the arguments for a format spec with %n$
 *
 * If format is valid, return true and fill argvalues[i] with the value
 * for the conversion spec that has %i$ or *i$.  Else return false.
 */

static bool
find_arguments(const char *format, va_list args,
      PrintfArgValue *argvalues)
{
 int   ch;
 bool  afterstar;
 int   accum;
 int   longlongflag;
 int   longflag;
 int   fmtpos;
 int   i;
 int   last_dollar = 0/* Init to "no dollar arguments known" */
 PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0};

 /*
  * This loop must accept the same format strings as the one in dopr().
  * However, we don't need to analyze them to the same level of detail.
  *
  * Since we're only called if there's a dollar-type spec somewhere, we can
  * fail immediately if we find a non-dollar spec.  Per the C99 standard,
  * all argument references in the format string must be one or the other.
 */

 while (*format != '\0')
 {
  /* Locate next conversion specifier */
  if (*format != '%')
  {
   /* Unlike dopr, we can just quit if there's no more specifiers */
   format = strchr(format + 1'%');
   if (format == NULL)
    break;
  }

  /* Process conversion spec starting at *format */
  format++;
  longflag = longlongflag = 0;
  fmtpos = accum = 0;
  afterstar = false;
nextch1:
  ch = *format++;
  switch (ch)
  {
   case '-':
   case '+':
    goto nextch1;
   case '0':
   case '1':
   case '2':
   case '3':
   case '4':
   case '5':
   case '6':
   case '7':
   case '8':
   case '9':
    accum = accum * 10 + (ch - '0');
    goto nextch1;
   case '.':
    accum = 0;
    goto nextch1;
   case '*':
    if (afterstar)
     return false/* previous star missing dollar */
    afterstar = true;
    accum = 0;
    goto nextch1;
   case '$':
    if (accum <= 0 || accum > PG_NL_ARGMAX)
     return false;
    if (afterstar)
    {
     if (argtypes[accum] &&
      argtypes[accum] != ATYPE_INT)
      return false;
     argtypes[accum] = ATYPE_INT;
     last_dollar = Max(last_dollar, accum);
     afterstar = false;
    }
    else
     fmtpos = accum;
    accum = 0;
    goto nextch1;
#ifdef WIN32
   case 'I':
    /* Windows PRI*{32,64,PTR} size */
    if (format[0] == '3' && format[1] == '2')
     format += 2;
    else if (format[0] == '6' && format[1] == '4')
    {
     format += 2;
     longlongflag = 1;
    }
    else
    {
#if SIZEOF_VOID_P == SIZEOF_LONG
     longflag = 1;
#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
     longlongflag = 1;
#else
#error "cannot find integer type of the same size as intptr_t"
#endif
    }
    goto nextch1;
#endif
   case 'l':
    if (longflag)
     longlongflag = 1;
    else
     longflag = 1;
    goto nextch1;
   case 'z':
#if SIZEOF_SIZE_T == SIZEOF_LONG
    longflag = 1;
#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
    longlongflag = 1;
#else
#error "cannot find integer type of the same size as size_t"
#endif
    goto nextch1;
   case 'h':
   case '\'':
    /* ignore these */
    goto nextch1;
   case 'd':
   case 'i':
   case 'o':
   case 'u':
   case 'x':
   case 'X':
    if (fmtpos)
    {
     PrintfArgType atype;

     if (longlongflag)
      atype = ATYPE_LONGLONG;
     else if (longflag)
      atype = ATYPE_LONG;
     else
      atype = ATYPE_INT;
     if (argtypes[fmtpos] &&
      argtypes[fmtpos] != atype)
      return false;
     argtypes[fmtpos] = atype;
     last_dollar = Max(last_dollar, fmtpos);
    }
    else
     return false/* non-dollar conversion spec */
    break;
   case 'c':
    if (fmtpos)
    {
     if (argtypes[fmtpos] &&
      argtypes[fmtpos] != ATYPE_INT)
      return false;
     argtypes[fmtpos] = ATYPE_INT;
     last_dollar = Max(last_dollar, fmtpos);
    }
    else
     return false/* non-dollar conversion spec */
    break;
   case 's':
   case 'p':
    if (fmtpos)
    {
     if (argtypes[fmtpos] &&
      argtypes[fmtpos] != ATYPE_CHARPTR)
      return false;
     argtypes[fmtpos] = ATYPE_CHARPTR;
     last_dollar = Max(last_dollar, fmtpos);
    }
    else
     return false/* non-dollar conversion spec */
    break;
   case 'e':
   case 'E':
   case 'f':
   case 'g':
   case 'G':
    if (fmtpos)
    {
     if (argtypes[fmtpos] &&
      argtypes[fmtpos] != ATYPE_DOUBLE)
      return false;
     argtypes[fmtpos] = ATYPE_DOUBLE;
     last_dollar = Max(last_dollar, fmtpos);
    }
    else
     return false/* non-dollar conversion spec */
    break;
   case 'm':
   case '%':
    break;
   default:
    return false/* bogus format string */
  }

  /*
   * If we finish the spec with afterstar still set, there's a
   * non-dollar star in there.
 */

  if (afterstar)
   return false;  /* non-dollar conversion spec */
 }

 /*
  * Format appears valid so far, so collect the arguments in physical
  * order.  (Since we rejected any non-dollar specs that would have
  * collected arguments, we know that dopr() hasn't collected any yet.)
 */

 for (i = 1; i <= last_dollar; i++)
 {
  switch (argtypes[i])
  {
   case ATYPE_NONE:
    return false;
   case ATYPE_INT:
    argvalues[i].i = va_arg(args, int);
    break;
   case ATYPE_LONG:
    argvalues[i].l = va_arg(args, long);
    break;
   case ATYPE_LONGLONG:
    argvalues[i].ll = va_arg(args, long long);
    break;
   case ATYPE_DOUBLE:
    argvalues[i].d = va_arg(args, double);
    break;
   case ATYPE_CHARPTR:
    argvalues[i].cptr = va_arg(args, char *);
    break;
  }
 }

 return true;
}

static void
fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
    int pointflag, PrintfTarget *target)
{
 int   padlen,
    vallen;   /* amount to pad */

 /*
  * If a maxwidth (precision) is specified, we must not fetch more bytes
  * than that.
 */

 if (pointflag)
  vallen = strnlen(value, maxwidth);
 else
  vallen = strlen(value);

 padlen = compute_padlen(minlen, vallen, leftjust);

 if (padlen > 0)
 {
  dopr_outchmulti(' ', padlen, target);
  padlen = 0;
 }

 dostr(value, vallen, target);

 trailing_pad(padlen, target);
}

static void
fmtptr(const void *value, PrintfTarget *target)
{
 int   vallen;
 char  convert[64];

 /* we rely on regular C library's snprintf to do the basic conversion */
 vallen = snprintf(convert, sizeof(convert), "%p", value);
 if (vallen < 0)
  target->failed = true;
 else
  dostr(convert, vallen, target);
}

static void
fmtint(long long value, char type, int forcesign, int leftjust,
    int minlen, int zpad, int precision, int pointflag,
    PrintfTarget *target)
{
 unsigned long long uvalue;
 int   base;
 int   dosign;
 const char *cvt = "0123456789abcdef";
 int   signvalue = 0;
 char  convert[64];
 int   vallen = 0;
 int   padlen;   /* amount to pad */
 int   zeropad;  /* extra leading zeroes */

 switch (type)
 {
  case 'd':
  case 'i':
   base = 10;
   dosign = 1;
   break;
  case 'o':
   base = 8;
   dosign = 0;
   break;
  case 'u':
   base = 10;
   dosign = 0;
   break;
  case 'x':
   base = 16;
   dosign = 0;
   break;
  case 'X':
   cvt = "0123456789ABCDEF";
   base = 16;
   dosign = 0;
   break;
  default:
   return;    /* keep compiler quiet */
 }

 /* disable MSVC warning about applying unary minus to an unsigned value */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4146)
#endif
 /* Handle +/- */
 if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
  uvalue = -(unsigned long long) value;
 else
  uvalue = (unsigned long long) value;
#ifdef _MSC_VER
#pragma warning(pop)
#endif

 /*
  * SUS: the result of converting 0 with an explicit precision of 0 is no
  * characters
 */

 if (value == 0 && pointflag && precision == 0)
  vallen = 0;
 else
 {
  /*
   * Convert integer to string.  We special-case each of the possible
   * base values so as to avoid general-purpose divisions.  On most
   * machines, division by a fixed constant can be done much more
   * cheaply than a general divide.
 */

  if (base == 10)
  {
   do
   {
    convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
    uvalue = uvalue / 10;
   } while (uvalue);
  }
  else if (base == 16)
  {
   do
   {
    convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
    uvalue = uvalue / 16;
   } while (uvalue);
  }
  else     /* base == 8 */
  {
   do
   {
    convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
    uvalue = uvalue / 8;
   } while (uvalue);
  }
 }

 zeropad = Max(0, precision - vallen);

 padlen = compute_padlen(minlen, vallen + zeropad, leftjust);

 leading_pad(zpad, signvalue, &padlen, target);

 if (zeropad > 0)
  dopr_outchmulti('0', zeropad, target);

 dostr(convert + sizeof(convert) - vallen, vallen, target);

 trailing_pad(padlen, target);
}

static void
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
{
 int   padlen;   /* amount to pad */

 padlen = compute_padlen(minlen, 1, leftjust);

 if (padlen > 0)
 {
  dopr_outchmulti(' ', padlen, target);
  padlen = 0;
 }

 dopr_outch(value, target);

 trailing_pad(padlen, target);
}

static void
fmtfloat(double value, char type, int forcesign, int leftjust,
   int minlen, int zpad, int precision, int pointflag,
   PrintfTarget *target)
{
 int   signvalue = 0;
 int   prec;
 int   vallen;
 char  fmt[8];
 char  convert[1024];
 int   zeropadlen = 0/* amount to pad with zeroes */
 int   padlen;   /* amount to pad with spaces */

 /*
  * We rely on the regular C library's snprintf to do the basic conversion,
  * then handle padding considerations here.
  *
  * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
  * too wildly more than that with other hardware.  In "f" format, snprintf
  * could therefore generate at most 308 characters to the left of the
  * decimal point; while we need to allow the precision to get as high as
  * 308+17 to ensure that we don't truncate significant digits from very
  * small values.  To handle both these extremes, we use a buffer of 1024
  * bytes and limit requested precision to 350 digits; this should prevent
  * buffer overrun even with non-IEEE math.  If the original precision
  * request was more than 350, separately pad with zeroes.
  *
  * We handle infinities and NaNs specially to ensure platform-independent
  * output.
 */

 if (precision < 0)   /* cover possible overflow of "accum" */
  precision = 0;
 prec = Min(precision, 350);

 if (isnan(value))
 {
  strcpy(convert, "NaN");
  vallen = 3;
  /* no zero padding, regardless of precision spec */
 }
 else
 {
  /*
   * Handle sign (NaNs have no sign, so we don't do this in the case
   * above).  "value < 0.0" will not be true for IEEE minus zero, so we
   * detect that by looking for the case where value equals 0.0
   * according to == but not according to memcmp.
 */

  static const double dzero = 0.0;

  if (adjust_sign((value < 0.0 ||
       (value == 0.0 &&
        memcmp(&value, &dzero, sizeof(double)) != 0)),
      forcesign, &signvalue))
   value = -value;

  if (isinf(value))
  {
   strcpy(convert, "Infinity");
   vallen = 8;
   /* no zero padding, regardless of precision spec */
  }
  else if (pointflag)
  {
   zeropadlen = precision - prec;
   fmt[0] = '%';
   fmt[1] = '.';
   fmt[2] = '*';
   fmt[3] = type;
   fmt[4] = '\0';
   vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
  }
  else
  {
   fmt[0] = '%';
   fmt[1] = type;
   fmt[2] = '\0';
   vallen = snprintf(convert, sizeof(convert), fmt, value);
  }
  if (vallen < 0)
   goto fail;

  /*
   * Windows, alone among our supported platforms, likes to emit
   * three-digit exponent fields even when two digits would do.  Hack
   * such results to look like the way everyone else does it.
 */

#ifdef WIN32
  if (vallen >= 6 &&
   convert[vallen - 5] == 'e' &&
   convert[vallen - 3] == '0')
  {
   convert[vallen - 3] = convert[vallen - 2];
   convert[vallen - 2] = convert[vallen - 1];
   vallen--;
  }
#endif
 }

 padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);

 leading_pad(zpad, signvalue, &padlen, target);

 if (zeropadlen > 0)
 {
  /* If 'e' or 'E' format, inject zeroes before the exponent */
  char    *epos = strrchr(convert, 'e');

  if (!epos)
   epos = strrchr(convert, 'E');
  if (epos)
  {
   /* pad before exponent */
   dostr(convert, epos - convert, target);
   dopr_outchmulti('0', zeropadlen, target);
   dostr(epos, vallen - (epos - convert), target);
  }
  else
  {
   /* no exponent, pad after the digits */
   dostr(convert, vallen, target);
   dopr_outchmulti('0', zeropadlen, target);
  }
 }
 else
 {
  /* no zero padding, just emit the number as-is */
  dostr(convert, vallen, target);
 }

 trailing_pad(padlen, target);
 return;

fail:
 target->failed = true;
}

/*
 * Nonstandard entry point to print a double value efficiently.
 *
 * This is approximately equivalent to strfromd(), but has an API more
 * adapted to what float8out() wants.  The behavior is like snprintf()
 * with a format of "%.ng", where n is the specified precision.
 * However, the target buffer must be nonempty (i.e. count > 0), and
 * the precision is silently bounded to a sane range.
 */

int
pg_strfromd(char *str, size_t count, int precision, double value)
{
 PrintfTarget target;
 int   signvalue = 0;
 int   vallen;
 char  fmt[8];
 char  convert[64];

 /* Set up the target like pg_snprintf, but require nonempty buffer */
 Assert(count > 0);
 target.bufstart = target.bufptr = str;
 target.bufend = str + count - 1;
 target.stream = NULL;
 target.nchars = 0;
 target.failed = false;

 /*
  * We bound precision to a reasonable range; the combination of this and
  * the knowledge that we're using "g" format without padding allows the
  * convert[] buffer to be reasonably small.
 */

 if (precision < 1)
  precision = 1;
 else if (precision > 32)
  precision = 32;

 /*
  * The rest is just an inlined version of the fmtfloat() logic above,
  * simplified using the knowledge that no padding is wanted.
 */

 if (isnan(value))
 {
  strcpy(convert, "NaN");
  vallen = 3;
 }
 else
 {
  static const double dzero = 0.0;

  if (value < 0.0 ||
   (value == 0.0 &&
    memcmp(&value, &dzero, sizeof(double)) != 0))
  {
   signvalue = '-';
   value = -value;
  }

  if (isinf(value))
  {
   strcpy(convert, "Infinity");
   vallen = 8;
  }
  else
  {
   fmt[0] = '%';
   fmt[1] = '.';
   fmt[2] = '*';
   fmt[3] = 'g';
   fmt[4] = '\0';
   vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
   if (vallen < 0)
   {
    target.failed = true;
    goto fail;
   }

#ifdef WIN32
   if (vallen >= 6 &&
    convert[vallen - 5] == 'e' &&
    convert[vallen - 3] == '0')
   {
    convert[vallen - 3] = convert[vallen - 2];
    convert[vallen - 2] = convert[vallen - 1];
    vallen--;
   }
#endif
  }
 }

 if (signvalue)
  dopr_outch(signvalue, &target);

 dostr(convert, vallen, &target);

fail:
 *(target.bufptr) = '\0';
 return target.failed ? -1 : (target.bufptr - target.bufstart
         + target.nchars);
}


static void
dostr(const char *str, int slen, PrintfTarget *target)
{
 /* fast path for common case of slen == 1 */
 if (slen == 1)
 {
  dopr_outch(*str, target);
  return;
 }

 while (slen > 0)
 {
  int   avail;

  if (target->bufend != NULL)
   avail = target->bufend - target->bufptr;
  else
   avail = slen;
  if (avail <= 0)
  {
   /* buffer full, can we dump to stream? */
   if (target->stream == NULL)
   {
    target->nchars += slen; /* no, lose the data */
    return;
   }
   flushbuffer(target);
   continue;
  }
  avail = Min(avail, slen);
  memmove(target->bufptr, str, avail);
  target->bufptr += avail;
  str += avail;
  slen -= avail;
 }
}

static void
dopr_outch(int c, PrintfTarget *target)
{
 if (target->bufend != NULL && target->bufptr >= target->bufend)
 {
  /* buffer full, can we dump to stream? */
  if (target->stream == NULL)
  {
   target->nchars++; /* no, lose the data */
   return;
  }
  flushbuffer(target);
 }
 *(target->bufptr++) = c;
}

static void
dopr_outchmulti(int c, int slen, PrintfTarget *target)
{
 /* fast path for common case of slen == 1 */
 if (slen == 1)
 {
  dopr_outch(c, target);
  return;
 }

 while (slen > 0)
 {
  int   avail;

  if (target->bufend != NULL)
   avail = target->bufend - target->bufptr;
  else
   avail = slen;
  if (avail <= 0)
  {
   /* buffer full, can we dump to stream? */
   if (target->stream == NULL)
   {
    target->nchars += slen; /* no, lose the data */
    return;
   }
   flushbuffer(target);
   continue;
  }
  avail = Min(avail, slen);
  memset(target->bufptr, c, avail);
  target->bufptr += avail;
  slen -= avail;
 }
}


static int
adjust_sign(int is_negative, int forcesign, int *signvalue)
{
 if (is_negative)
 {
  *signvalue = '-';
  return true;
 }
 else if (forcesign)
  *signvalue = '+';
 return false;
}


static int
compute_padlen(int minlen, int vallen, int leftjust)
{
 int   padlen;

 padlen = minlen - vallen;
 if (padlen < 0)
  padlen = 0;
 if (leftjust)
  padlen = -padlen;
 return padlen;
}


static void
leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
{
 int   maxpad;

 if (*padlen > 0 && zpad)
 {
  if (signvalue)
  {
   dopr_outch(signvalue, target);
   --(*padlen);
   signvalue = 0;
  }
  if (*padlen > 0)
  {
   dopr_outchmulti(zpad, *padlen, target);
   *padlen = 0;
  }
 }
 maxpad = (signvalue != 0);
 if (*padlen > maxpad)
 {
  dopr_outchmulti(' ', *padlen - maxpad, target);
  *padlen = maxpad;
 }
 if (signvalue)
 {
  dopr_outch(signvalue, target);
  if (*padlen > 0)
   --(*padlen);
  else if (*padlen < 0)
   ++(*padlen);
 }
}


static void
trailing_pad(int padlen, PrintfTarget *target)
{
 if (padlen < 0)
  dopr_outchmulti(' ', -padlen, target);
}

Messung V0.5 in Prozent
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.32 Sekunden  (vorverarbeitet am  2026-08-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.