LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/python3/Modules/_decimal/libmpdec - io.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 0 663 0.0 %
Date: 2012-12-17 Functions: 0 33 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2008-2012 Stefan Krah. All rights reserved.
       3             :  *
       4             :  * Redistribution and use in source and binary forms, with or without
       5             :  * modification, are permitted provided that the following conditions
       6             :  * are met:
       7             :  *
       8             :  * 1. Redistributions of source code must retain the above copyright
       9             :  *    notice, this list of conditions and the following disclaimer.
      10             :  *
      11             :  * 2. Redistributions in binary form must reproduce the above copyright
      12             :  *    notice, this list of conditions and the following disclaimer in the
      13             :  *    documentation and/or other materials provided with the distribution.
      14             :  *
      15             :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
      16             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      17             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      18             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      19             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      20             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      21             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      22             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      23             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      24             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      25             :  * SUCH DAMAGE.
      26             :  */
      27             : 
      28             : 
      29             : #include "mpdecimal.h"
      30             : #include <stdio.h>
      31             : #include <stdlib.h>
      32             : #include <string.h>
      33             : #include <ctype.h>
      34             : #include <limits.h>
      35             : #include <assert.h>
      36             : #include <errno.h>
      37             : #include <locale.h>
      38             : #include "bits.h"
      39             : #include "constants.h"
      40             : #include "memory.h"
      41             : #include "typearith.h"
      42             : #include "io.h"
      43             : 
      44             : 
      45             : /* This file contains functions for decimal <-> string conversions, including
      46             :    PEP-3101 formatting for numeric types. */
      47             : 
      48             : 
      49             : /*
      50             :  * Work around the behavior of tolower() and strcasecmp() in certain
      51             :  * locales. For example, in tr_TR.utf8:
      52             :  *
      53             :  * tolower((unsigned char)'I') == 'I'
      54             :  *
      55             :  * u is the exact uppercase version of l; n is strlen(l) or strlen(l)+1
      56             :  */
      57             : static inline int
      58           0 : _mpd_strneq(const char *s, const char *l, const char *u, size_t n)
      59             : {
      60           0 :     while (--n != SIZE_MAX) {
      61           0 :         if (*s != *l && *s != *u) {
      62           0 :             return 0;
      63             :         }
      64           0 :         s++; u++; l++;
      65             :     }
      66             : 
      67           0 :     return 1;
      68             : }
      69             : 
      70             : static mpd_ssize_t
      71           0 : strtoexp(const char *s)
      72             : {
      73             :     char *end;
      74             :     mpd_ssize_t retval;
      75             : 
      76           0 :     errno = 0;
      77           0 :     retval = mpd_strtossize(s, &end, 10);
      78           0 :     if (errno == 0 && !(*s != '\0' && *end == '\0'))
      79           0 :         errno = EINVAL;
      80             : 
      81           0 :     return retval;
      82             : }
      83             : 
      84             : /*
      85             :  * Scan 'len' words. The most significant word contains 'r' digits,
      86             :  * the remaining words are full words. Skip dpoint. The string 's' must
      87             :  * consist of digits and an optional single decimal point at 'dpoint'.
      88             :  */
      89             : static void
      90           0 : string_to_coeff(mpd_uint_t *data, const char *s, const char *dpoint, int r,
      91             :                 size_t len)
      92             : {
      93             :     int j;
      94             : 
      95           0 :     if (r > 0) {
      96           0 :         data[--len] = 0;
      97           0 :         for (j = 0; j < r; j++, s++) {
      98           0 :             if (s == dpoint) s++;
      99           0 :             data[len] = 10 * data[len] + (*s - '0');
     100             :         }
     101             :     }
     102             : 
     103           0 :     while (--len != SIZE_MAX) {
     104           0 :         data[len] = 0;
     105           0 :         for (j = 0; j < MPD_RDIGITS; j++, s++) {
     106           0 :             if (s == dpoint) s++;
     107           0 :             data[len] = 10 * data[len] + (*s - '0');
     108             :         }
     109             :     }
     110           0 : }
     111             : 
     112             : /*
     113             :  * Partially verify a numeric string of the form:
     114             :  *
     115             :  *     [cdigits][.][cdigits][eE][+-][edigits]
     116             :  *
     117             :  * If successful, return a pointer to the location of the first
     118             :  * relevant coefficient digit. This digit is either non-zero or
     119             :  * part of one of the following patterns:
     120             :  *
     121             :  *     ["0\x00", "0.\x00", "0.E", "0.e", "0E", "0e"]
     122             :  *
     123             :  * The locations of a single optional dot or indicator are stored
     124             :  * in 'dpoint' and 'exp'.
     125             :  *
     126             :  * The end of the string is stored in 'end'. If an indicator [eE]
     127             :  * occurs without trailing [edigits], the condition is caught
     128             :  * later by strtoexp().
     129             :  */
     130             : static const char *
     131           0 : scan_dpoint_exp(const char *s, const char **dpoint, const char **exp,
     132             :                 const char **end)
     133             : {
     134           0 :     const char *coeff = NULL;
     135             : 
     136           0 :     *dpoint = NULL;
     137           0 :     *exp = NULL;
     138           0 :     for (; *s != '\0'; s++) {
     139           0 :         switch (*s) {
     140             :         case '.':
     141           0 :             if (*dpoint != NULL || *exp != NULL)
     142           0 :                 return NULL;
     143           0 :             *dpoint = s;
     144           0 :             break;
     145             :         case 'E': case 'e':
     146           0 :             if (*exp != NULL)
     147           0 :                 return NULL;
     148           0 :             *exp = s;
     149           0 :             if (*(s+1) == '+' || *(s+1) == '-')
     150           0 :                 s++;
     151           0 :             break;
     152             :         default:
     153           0 :             if (!isdigit((uchar)*s))
     154           0 :                 return NULL;
     155           0 :             if (coeff == NULL && *exp == NULL) {
     156           0 :                 if (*s == '0') {
     157           0 :                     if (!isdigit((uchar)*(s+1)))
     158           0 :                         if (!(*(s+1) == '.' &&
     159           0 :                               isdigit((uchar)*(s+2))))
     160           0 :                             coeff = s;
     161             :                 }
     162             :                 else {
     163           0 :                     coeff = s;
     164             :                 }
     165             :             }
     166           0 :             break;
     167             : 
     168             :         }
     169             :     }
     170             : 
     171           0 :     *end = s;
     172           0 :     return coeff;
     173             : }
     174             : 
     175             : /* scan the payload of a NaN */
     176             : static const char *
     177           0 : scan_payload(const char *s, const char **end)
     178             : {
     179             :     const char *coeff;
     180             : 
     181           0 :     while (*s == '0')
     182           0 :         s++;
     183           0 :     coeff = s;
     184             : 
     185           0 :     while (isdigit((uchar)*s))
     186           0 :         s++;
     187           0 :     *end = s;
     188             : 
     189           0 :     return (*s == '\0') ? coeff : NULL;
     190             : }
     191             : 
     192             : /* convert a character string to a decimal */
     193             : void
     194           0 : mpd_qset_string(mpd_t *dec, const char *s, const mpd_context_t *ctx,
     195             :                 uint32_t *status)
     196             : {
     197             :     mpd_ssize_t q, r, len;
     198             :     const char *coeff, *end;
     199           0 :     const char *dpoint = NULL, *exp = NULL;
     200             :     size_t digits;
     201           0 :     uint8_t sign = MPD_POS;
     202             : 
     203           0 :     mpd_set_flags(dec, 0);
     204           0 :     dec->len = 0;
     205           0 :     dec->exp = 0;
     206             : 
     207             :     /* sign */
     208           0 :     if (*s == '+') {
     209           0 :         s++;
     210             :     }
     211           0 :     else if (*s == '-') {
     212           0 :         mpd_set_negative(dec);
     213           0 :         sign = MPD_NEG;
     214           0 :         s++;
     215             :     }
     216             : 
     217           0 :     if (_mpd_strneq(s, "nan", "NAN", 3)) { /* NaN */
     218           0 :         s += 3;
     219           0 :         mpd_setspecial(dec, sign, MPD_NAN);
     220           0 :         if (*s == '\0')
     221             :             return;
     222             :         /* validate payload: digits only */
     223           0 :         if ((coeff = scan_payload(s, &end)) == NULL)
     224           0 :             goto conversion_error;
     225             :         /* payload consists entirely of zeros */
     226           0 :         if (*coeff == '\0')
     227             :             return;
     228           0 :         digits = end - coeff;
     229             :         /* prec >= 1, clamp is 0 or 1 */
     230           0 :         if (digits > (size_t)(ctx->prec-ctx->clamp))
     231           0 :             goto conversion_error;
     232             :     } /* sNaN */
     233           0 :     else if (_mpd_strneq(s, "snan", "SNAN", 4)) {
     234           0 :         s += 4;
     235           0 :         mpd_setspecial(dec, sign, MPD_SNAN);
     236           0 :         if (*s == '\0')
     237             :             return;
     238             :         /* validate payload: digits only */
     239           0 :         if ((coeff = scan_payload(s, &end)) == NULL)
     240           0 :             goto conversion_error;
     241             :         /* payload consists entirely of zeros */
     242           0 :         if (*coeff == '\0')
     243             :             return;
     244           0 :         digits = end - coeff;
     245           0 :         if (digits > (size_t)(ctx->prec-ctx->clamp))
     246           0 :             goto conversion_error;
     247             :     }
     248           0 :     else if (_mpd_strneq(s, "inf", "INF", 3)) {
     249           0 :         s += 3;
     250           0 :         if (*s == '\0' || _mpd_strneq(s, "inity", "INITY", 6)) {
     251             :             /* numeric-value: infinity */
     252           0 :             mpd_setspecial(dec, sign, MPD_INF);
     253             :             return;
     254             :         }
     255           0 :         goto conversion_error;
     256             :     }
     257             :     else {
     258             :         /* scan for start of coefficient, decimal point, indicator, end */
     259           0 :         if ((coeff = scan_dpoint_exp(s, &dpoint, &exp, &end)) == NULL)
     260           0 :             goto conversion_error;
     261             : 
     262             :         /* numeric-value: [exponent-part] */
     263           0 :         if (exp) {
     264             :             /* exponent-part */
     265           0 :             end = exp; exp++;
     266           0 :             dec->exp = strtoexp(exp);
     267           0 :             if (errno) {
     268           0 :                 if (!(errno == ERANGE &&
     269           0 :                      (dec->exp == MPD_SSIZE_MAX ||
     270           0 :                       dec->exp == MPD_SSIZE_MIN)))
     271             :                     goto conversion_error;
     272             :             }
     273             :         }
     274             : 
     275           0 :             digits = end - coeff;
     276           0 :         if (dpoint) {
     277           0 :             size_t fracdigits = end-dpoint-1;
     278           0 :             if (dpoint > coeff) digits--;
     279             : 
     280           0 :             if (fracdigits > MPD_MAX_PREC) {
     281           0 :                 goto conversion_error;
     282             :             }
     283           0 :             if (dec->exp < MPD_SSIZE_MIN+(mpd_ssize_t)fracdigits) {
     284           0 :                 dec->exp = MPD_SSIZE_MIN;
     285             :             }
     286             :             else {
     287           0 :                 dec->exp -= (mpd_ssize_t)fracdigits;
     288             :             }
     289             :         }
     290           0 :         if (digits > MPD_MAX_PREC) {
     291           0 :             goto conversion_error;
     292             :         }
     293           0 :         if (dec->exp > MPD_EXP_INF) {
     294           0 :             dec->exp = MPD_EXP_INF;
     295             :         }
     296           0 :         if (dec->exp == MPD_SSIZE_MIN) {
     297           0 :             dec->exp = MPD_SSIZE_MIN+1;
     298             :         }
     299             :     }
     300             : 
     301           0 :     _mpd_idiv_word(&q, &r, (mpd_ssize_t)digits, MPD_RDIGITS);
     302             : 
     303           0 :     len = (r == 0) ? q : q+1;
     304           0 :     if (len == 0) {
     305           0 :         goto conversion_error; /* GCOV_NOT_REACHED */
     306             :     }
     307           0 :     if (!mpd_qresize(dec, len, status)) {
     308           0 :         mpd_seterror(dec, MPD_Malloc_error, status);
     309             :         return;
     310             :     }
     311           0 :     dec->len = len;
     312             : 
     313           0 :     string_to_coeff(dec->data, coeff, dpoint, (int)r, len);
     314             : 
     315           0 :     mpd_setdigits(dec);
     316           0 :     mpd_qfinalize(dec, ctx, status);
     317             :     return;
     318             : 
     319             : conversion_error:
     320             :     /* standard wants a positive NaN */
     321           0 :     mpd_seterror(dec, MPD_Conversion_syntax, status);
     322             : }
     323             : 
     324             : /* Print word x with n decimal digits to string s. dot is either NULL
     325             :    or the location of a decimal point. */
     326             : #define EXTRACT_DIGIT(s, x, d, dot) \
     327             :         if (s == dot) *s++ = '.'; *s++ = '0' + (char)(x / d); x %= d
     328             : static inline char *
     329           0 : word_to_string(char *s, mpd_uint_t x, int n, char *dot)
     330             : {
     331           0 :     switch(n) {
     332             : #ifdef CONFIG_64
     333             :     case 20: EXTRACT_DIGIT(s, x, 10000000000000000000ULL, dot); /* GCOV_NOT_REACHED */
     334             :     case 19: EXTRACT_DIGIT(s, x, 1000000000000000000ULL, dot);
     335             :     case 18: EXTRACT_DIGIT(s, x, 100000000000000000ULL, dot);
     336             :     case 17: EXTRACT_DIGIT(s, x, 10000000000000000ULL, dot);
     337             :     case 16: EXTRACT_DIGIT(s, x, 1000000000000000ULL, dot);
     338             :     case 15: EXTRACT_DIGIT(s, x, 100000000000000ULL, dot);
     339             :     case 14: EXTRACT_DIGIT(s, x, 10000000000000ULL, dot);
     340             :     case 13: EXTRACT_DIGIT(s, x, 1000000000000ULL, dot);
     341             :     case 12: EXTRACT_DIGIT(s, x, 100000000000ULL, dot);
     342             :     case 11: EXTRACT_DIGIT(s, x, 10000000000ULL, dot);
     343             : #endif
     344           0 :     case 10: EXTRACT_DIGIT(s, x, 1000000000UL, dot);
     345           0 :     case 9:  EXTRACT_DIGIT(s, x, 100000000UL, dot);
     346           0 :     case 8:  EXTRACT_DIGIT(s, x, 10000000UL, dot);
     347           0 :     case 7:  EXTRACT_DIGIT(s, x, 1000000UL, dot);
     348           0 :     case 6:  EXTRACT_DIGIT(s, x, 100000UL, dot);
     349           0 :     case 5:  EXTRACT_DIGIT(s, x, 10000UL, dot);
     350           0 :     case 4:  EXTRACT_DIGIT(s, x, 1000UL, dot);
     351           0 :     case 3:  EXTRACT_DIGIT(s, x, 100UL, dot);
     352           0 :     case 2:  EXTRACT_DIGIT(s, x, 10UL, dot);
     353           0 :     default: if (s == dot) *s++ = '.'; *s++ = '0' + (char)x;
     354             :     }
     355             :  
     356           0 :     *s = '\0';
     357           0 :     return s;
     358             : }
     359             : 
     360             : /* Print exponent x to string s. Undefined for MPD_SSIZE_MIN. */
     361             : static inline char *
     362           0 : exp_to_string(char *s, mpd_ssize_t x)
     363             : {
     364           0 :     char sign = '+';
     365             : 
     366           0 :     if (x < 0) {
     367           0 :         sign = '-';
     368           0 :         x = -x;
     369             :     }
     370           0 :     *s++ = sign;
     371             : 
     372           0 :     return word_to_string(s, x, mpd_word_digits(x), NULL);
     373             : }
     374             : 
     375             : /* Print the coefficient of dec to string s. len(dec) > 0. */
     376             : static inline char *
     377           0 : coeff_to_string(char *s, const mpd_t *dec)
     378             : {
     379             :     mpd_uint_t x;
     380             :     mpd_ssize_t i;
     381             : 
     382             :     /* most significant word */
     383           0 :     x = mpd_msword(dec);
     384           0 :     s = word_to_string(s, x, mpd_word_digits(x), NULL);
     385             : 
     386             :     /* remaining full words */
     387           0 :     for (i=dec->len-2; i >= 0; --i) {
     388           0 :         x = dec->data[i];
     389           0 :         s = word_to_string(s, x, MPD_RDIGITS, NULL);
     390             :     }
     391             : 
     392           0 :     return s;
     393             : }
     394             : 
     395             : /* Print the coefficient of dec to string s. len(dec) > 0. dot is either
     396             :    NULL or a pointer to the location of a decimal point. */
     397             : static inline char *
     398           0 : coeff_to_string_dot(char *s, char *dot, const mpd_t *dec)
     399             : {
     400             :     mpd_uint_t x;
     401             :     mpd_ssize_t i;
     402             : 
     403             :     /* most significant word */
     404           0 :     x = mpd_msword(dec);
     405           0 :     s = word_to_string(s, x, mpd_word_digits(x), dot);
     406             : 
     407             :     /* remaining full words */
     408           0 :     for (i=dec->len-2; i >= 0; --i) {
     409           0 :         x = dec->data[i];
     410           0 :         s = word_to_string(s, x, MPD_RDIGITS, dot);
     411             :     }
     412             : 
     413           0 :     return s;
     414             : }
     415             : 
     416             : /* Format type */
     417             : #define MPD_FMT_LOWER      0x00000000
     418             : #define MPD_FMT_UPPER      0x00000001
     419             : #define MPD_FMT_TOSCI      0x00000002
     420             : #define MPD_FMT_TOENG      0x00000004
     421             : #define MPD_FMT_EXP        0x00000008
     422             : #define MPD_FMT_FIXED      0x00000010
     423             : #define MPD_FMT_PERCENT    0x00000020
     424             : #define MPD_FMT_SIGN_SPACE 0x00000040
     425             : #define MPD_FMT_SIGN_PLUS  0x00000080
     426             : 
     427             : /* Default place of the decimal point for MPD_FMT_TOSCI, MPD_FMT_EXP */
     428             : #define MPD_DEFAULT_DOTPLACE 1
     429             : 
     430             : /*
     431             :  * Set *result to the string representation of a decimal. Return the length
     432             :  * of *result, not including the terminating '\0' character.
     433             :  *
     434             :  * Formatting is done according to 'flags'. A return value of -1 with *result
     435             :  * set to NULL indicates MPD_Malloc_error.
     436             :  *
     437             :  * 'dplace' is the default place of the decimal point. It is always set to
     438             :  * MPD_DEFAULT_DOTPLACE except for zeros in combination with MPD_FMT_EXP.
     439             :  */
     440             : static mpd_ssize_t
     441           0 : _mpd_to_string(char **result, const mpd_t *dec, int flags, mpd_ssize_t dplace)
     442             : {
     443           0 :     char *decstring = NULL, *cp = NULL;
     444             :     mpd_ssize_t ldigits;
     445           0 :     mpd_ssize_t mem = 0, k;
     446             : 
     447           0 :     if (mpd_isspecial(dec)) {
     448             : 
     449           0 :         mem = sizeof "-Infinity";
     450           0 :         if (mpd_isnan(dec) && dec->len > 0) {
     451             :             /* diagnostic code */
     452           0 :             mem += dec->digits;
     453             :         }
     454           0 :         cp = decstring = mpd_alloc(mem, sizeof *decstring);
     455           0 :         if (cp == NULL) {
     456           0 :             *result = NULL;
     457           0 :             return -1;
     458             :         }
     459             : 
     460           0 :         if (mpd_isnegative(dec)) {
     461           0 :             *cp++ = '-';
     462             :         }
     463           0 :         else if (flags&MPD_FMT_SIGN_SPACE) {
     464           0 :             *cp++ = ' ';
     465             :         }
     466           0 :         else if (flags&MPD_FMT_SIGN_PLUS) {
     467           0 :             *cp++ = '+';
     468             :         }
     469             : 
     470           0 :         if (mpd_isnan(dec)) {
     471           0 :             if (mpd_isqnan(dec)) {
     472           0 :                 strcpy(cp, "NaN");
     473           0 :                 cp += 3;
     474             :             }
     475             :             else {
     476           0 :                 strcpy(cp, "sNaN");
     477           0 :                 cp += 4;
     478             :             }
     479           0 :             if (dec->len > 0) { /* diagnostic code */
     480           0 :                 cp = coeff_to_string(cp, dec);
     481             :             }
     482             :         }
     483           0 :         else if (mpd_isinfinite(dec)) {
     484           0 :             strcpy(cp, "Infinity");
     485           0 :             cp += 8;
     486             :         }
     487             :         else { /* debug */
     488           0 :             abort(); /* GCOV_NOT_REACHED */
     489             :         }
     490             :     }
     491             :     else {
     492             :         assert(dec->len > 0);
     493             : 
     494             :         /*
     495             :          * For easier manipulation of the decimal point's location
     496             :          * and the exponent that is finally printed, the number is
     497             :          * rescaled to a virtual representation with exp = 0. Here
     498             :          * ldigits denotes the number of decimal digits to the left
     499             :          * of the decimal point and remains constant once initialized.
     500             :          *
     501             :          * dplace is the location of the decimal point relative to
     502             :          * the start of the coefficient. Note that 3) always holds
     503             :          * when dplace is shifted.
     504             :          *
     505             :          *   1) ldigits := dec->digits - dec->exp
     506             :          *   2) dplace  := ldigits            (initially)
     507             :          *   3) exp     := ldigits - dplace   (initially exp = 0)
     508             :          *
     509             :          *   0.00000_.____._____000000.
     510             :          *    ^      ^    ^           ^
     511             :          *    |      |    |           |
     512             :          *    |      |    |           `- dplace >= digits
     513             :          *    |      |    `- dplace in the middle of the coefficient
     514             :          *    |      ` dplace = 1 (after the first coefficient digit)
     515             :          *    `- dplace <= 0
     516             :          */
     517             : 
     518           0 :         ldigits = dec->digits + dec->exp;
     519             : 
     520           0 :         if (flags&MPD_FMT_EXP) {
     521             :             ;
     522             :         }
     523           0 :         else if (flags&MPD_FMT_FIXED || (dec->exp <= 0 && ldigits > -6)) {
     524             :             /* MPD_FMT_FIXED: always use fixed point notation.
     525             :              * MPD_FMT_TOSCI, MPD_FMT_TOENG: for a certain range,
     526             :              * override exponent notation. */
     527           0 :             dplace = ldigits;
     528             :         }
     529           0 :         else if (flags&MPD_FMT_TOENG) {
     530           0 :             if (mpd_iszero(dec)) {
     531             :                 /* If the exponent is divisible by three,
     532             :                  * dplace = 1. Otherwise, move dplace one
     533             :                  * or two places to the left. */
     534           0 :                 dplace = -1 + mod_mpd_ssize_t(dec->exp+2, 3);
     535             :             }
     536             :             else { /* ldigits-1 is the adjusted exponent, which
     537             :                 * should be divisible by three. If not, move
     538             :                 * dplace one or two places to the right. */
     539           0 :                 dplace += mod_mpd_ssize_t(ldigits-1, 3);
     540             :             }
     541             :         }
     542             : 
     543             :         /*
     544             :          * Basic space requirements:
     545             :          *
     546             :          * [-][.][coeffdigits][E][-][expdigits+1][%]['\0']
     547             :          *
     548             :          * If the decimal point lies outside of the coefficient digits,
     549             :          * space is adjusted accordingly.
     550             :          */
     551           0 :         if (dplace <= 0) {
     552           0 :             mem = -dplace + dec->digits + 2;
     553             :         }
     554           0 :         else if (dplace >= dec->digits) {
     555           0 :             mem = dplace;
     556             :         }
     557             :         else {
     558           0 :             mem = dec->digits;
     559             :         }
     560           0 :         mem += (MPD_EXPDIGITS+1+6);
     561             : 
     562           0 :         cp = decstring = mpd_alloc(mem, sizeof *decstring);
     563           0 :         if (cp == NULL) {
     564           0 :             *result = NULL;
     565           0 :             return -1;
     566             :         }
     567             : 
     568             : 
     569           0 :         if (mpd_isnegative(dec)) {
     570           0 :             *cp++ = '-';
     571             :         }
     572           0 :         else if (flags&MPD_FMT_SIGN_SPACE) {
     573           0 :             *cp++ = ' ';
     574             :         }
     575           0 :         else if (flags&MPD_FMT_SIGN_PLUS) {
     576           0 :             *cp++ = '+';
     577             :         }
     578             : 
     579           0 :         if (dplace <= 0) {
     580             :             /* space: -dplace+dec->digits+2 */
     581           0 :             *cp++ = '0';
     582           0 :             *cp++ = '.';
     583           0 :             for (k = 0; k < -dplace; k++) {
     584           0 :                 *cp++ = '0';
     585             :             }
     586           0 :             cp = coeff_to_string(cp, dec);
     587             :         }
     588           0 :         else if (dplace >= dec->digits) {
     589             :             /* space: dplace */
     590           0 :             cp = coeff_to_string(cp, dec);
     591           0 :             for (k = 0; k < dplace-dec->digits; k++) {
     592           0 :                 *cp++ = '0';
     593             :             }
     594             :         }
     595             :         else {
     596             :             /* space: dec->digits+1 */
     597           0 :             cp = coeff_to_string_dot(cp, cp+dplace, dec);
     598             :         }
     599             : 
     600             :         /*
     601             :          * Conditions for printing an exponent:
     602             :          *
     603             :          *   MPD_FMT_TOSCI, MPD_FMT_TOENG: only if ldigits != dplace
     604             :          *   MPD_FMT_FIXED:                never (ldigits == dplace)
     605             :          *   MPD_FMT_EXP:                  always
     606             :          */
     607           0 :         if (ldigits != dplace || flags&MPD_FMT_EXP) {
     608             :             /* space: expdigits+2 */
     609           0 :             *cp++ = (flags&MPD_FMT_UPPER) ? 'E' : 'e';
     610           0 :             cp = exp_to_string(cp, ldigits-dplace);
     611             :         }
     612             : 
     613           0 :         if (flags&MPD_FMT_PERCENT) {
     614           0 :             *cp++ = '%';
     615             :         }
     616             :     }
     617             : 
     618             :     assert(cp < decstring+mem);
     619             :     assert(cp-decstring < MPD_SSIZE_MAX);
     620             : 
     621           0 :     *cp = '\0';
     622           0 :     *result = decstring;
     623           0 :     return (mpd_ssize_t)(cp-decstring);
     624             : }
     625             : 
     626             : char *
     627           0 : mpd_to_sci(const mpd_t *dec, int fmt)
     628             : {
     629             :     char *res;
     630           0 :     int flags = MPD_FMT_TOSCI;
     631             : 
     632           0 :     flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
     633           0 :     (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
     634           0 :     return res;
     635             : }
     636             : 
     637             : char *
     638           0 : mpd_to_eng(const mpd_t *dec, int fmt)
     639             : {
     640             :     char *res;
     641           0 :     int flags = MPD_FMT_TOENG;
     642             : 
     643           0 :     flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
     644           0 :     (void)_mpd_to_string(&res, dec, flags, MPD_DEFAULT_DOTPLACE);
     645           0 :     return res;
     646             : }
     647             : 
     648             : mpd_ssize_t
     649           0 : mpd_to_sci_size(char **res, const mpd_t *dec, int fmt)
     650             : {
     651           0 :     int flags = MPD_FMT_TOSCI;
     652             : 
     653           0 :     flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
     654           0 :     return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
     655             : }
     656             : 
     657             : mpd_ssize_t
     658           0 : mpd_to_eng_size(char **res, const mpd_t *dec, int fmt)
     659             : {
     660           0 :     int flags = MPD_FMT_TOENG;
     661             : 
     662           0 :     flags |= fmt ? MPD_FMT_UPPER : MPD_FMT_LOWER;
     663           0 :     return _mpd_to_string(res, dec, flags, MPD_DEFAULT_DOTPLACE);
     664             : }
     665             : 
     666             : /* Copy a single UTF-8 char to dest. See: The Unicode Standard, version 5.2,
     667             :    chapter 3.9: Well-formed UTF-8 byte sequences. */
     668             : static int
     669           0 : _mpd_copy_utf8(char dest[5], const char *s)
     670             : {
     671           0 :     const uchar *cp = (const uchar *)s;
     672             :     uchar lb, ub;
     673             :     int count, i;
     674             : 
     675             : 
     676           0 :     if (*cp == 0) {
     677             :         /* empty string */
     678           0 :         dest[0] = '\0';
     679           0 :         return 0;
     680             :     }
     681           0 :     else if (*cp <= 0x7f) {
     682             :         /* ascii */
     683           0 :         dest[0] = *cp;
     684           0 :         dest[1] = '\0';
     685           0 :         return 1;
     686             :     }
     687           0 :     else if (0xc2 <= *cp && *cp <= 0xdf) {
     688           0 :         lb = 0x80; ub = 0xbf;
     689           0 :         count = 2;
     690             :     }
     691           0 :     else if (*cp == 0xe0) {
     692           0 :         lb = 0xa0; ub = 0xbf;
     693           0 :         count = 3;
     694             :     }
     695           0 :     else if (*cp <= 0xec) {
     696           0 :         lb = 0x80; ub = 0xbf;
     697           0 :         count = 3;
     698             :     }
     699           0 :     else if (*cp == 0xed) {
     700           0 :         lb = 0x80; ub = 0x9f;
     701           0 :         count = 3;
     702             :     }
     703           0 :     else if (*cp <= 0xef) {
     704           0 :         lb = 0x80; ub = 0xbf;
     705           0 :         count = 3;
     706             :     }
     707           0 :     else if (*cp == 0xf0) {
     708           0 :         lb = 0x90; ub = 0xbf;
     709           0 :         count = 4;
     710             :     }
     711           0 :     else if (*cp <= 0xf3) {
     712           0 :         lb = 0x80; ub = 0xbf;
     713           0 :         count = 4;
     714             :     }
     715           0 :     else if (*cp == 0xf4) {
     716           0 :         lb = 0x80; ub = 0x8f;
     717           0 :         count = 4;
     718             :     }
     719             :     else {
     720             :         /* invalid */
     721           0 :         goto error;
     722             :     }
     723             : 
     724           0 :     dest[0] = *cp++;
     725           0 :     if (*cp < lb || ub < *cp) {
     726             :         goto error;
     727             :     }
     728           0 :     dest[1] = *cp++;
     729           0 :     for (i = 2; i < count; i++) {
     730           0 :         if (*cp < 0x80 || 0xbf < *cp) {
     731             :             goto error;
     732             :         }
     733           0 :         dest[i] = *cp++;
     734             :     }
     735           0 :     dest[i] = '\0';
     736             : 
     737           0 :     return count;
     738             : 
     739             : error:
     740           0 :     dest[0] = '\0';
     741           0 :     return -1;
     742             : }
     743             : 
     744             : int
     745           0 : mpd_validate_lconv(mpd_spec_t *spec)
     746             : {
     747             :     size_t n;
     748             : #if CHAR_MAX == SCHAR_MAX
     749           0 :     const char *cp = spec->grouping;
     750           0 :     while (*cp != '\0') {
     751           0 :         if (*cp++ < 0) {
     752           0 :             return -1;
     753             :         }
     754             :     }
     755             : #endif
     756           0 :     n = strlen(spec->dot);
     757           0 :     if (n == 0 || n > 4) {
     758           0 :         return -1;
     759             :     }
     760           0 :     if (strlen(spec->sep) > 4) {
     761           0 :         return -1;
     762             :     }
     763             : 
     764           0 :     return 0;
     765             : }
     766             : 
     767             : int
     768           0 : mpd_parse_fmt_str(mpd_spec_t *spec, const char *fmt, int caps)
     769             : {
     770           0 :     char *cp = (char *)fmt;
     771           0 :     int have_align = 0, n;
     772             : 
     773             :     /* defaults */
     774           0 :     spec->min_width = 0;
     775           0 :     spec->prec = -1;
     776           0 :     spec->type = caps ? 'G' : 'g';
     777           0 :     spec->align = '>';
     778           0 :     spec->sign = '-';
     779           0 :     spec->dot = "";
     780           0 :     spec->sep = "";
     781           0 :     spec->grouping = "";
     782             : 
     783             : 
     784             :     /* presume that the first character is a UTF-8 fill character */
     785           0 :     if ((n = _mpd_copy_utf8(spec->fill, cp)) < 0) {
     786           0 :         return 0;
     787             :     }
     788             : 
     789             :     /* alignment directive, prefixed by a fill character */
     790           0 :     if (*cp && (*(cp+n) == '<' || *(cp+n) == '>' ||
     791           0 :                 *(cp+n) == '=' || *(cp+n) == '^')) {
     792           0 :         cp += n;
     793           0 :         spec->align = *cp++;
     794           0 :         have_align = 1;
     795             :     } /* alignment directive */
     796             :     else {
     797             :         /* default fill character */
     798           0 :         spec->fill[0] = ' ';
     799           0 :         spec->fill[1] = '\0';
     800           0 :         if (*cp == '<' || *cp == '>' ||
     801           0 :             *cp == '=' || *cp == '^') {
     802           0 :             spec->align = *cp++;
     803           0 :             have_align = 1;
     804             :         }
     805             :     }
     806             : 
     807             :     /* sign formatting */
     808           0 :     if (*cp == '+' || *cp == '-' || *cp == ' ') {
     809           0 :         spec->sign = *cp++;
     810             :     }
     811             : 
     812             :     /* zero padding */
     813           0 :     if (*cp == '0') {
     814             :         /* zero padding implies alignment, which should not be
     815             :          * specified twice. */
     816           0 :         if (have_align) {
     817           0 :             return 0;
     818             :         }
     819           0 :         spec->align = 'z';
     820           0 :         spec->fill[0] = *cp++;
     821           0 :         spec->fill[1] = '\0';
     822             :     }
     823             : 
     824             :     /* minimum width */
     825           0 :     if (isdigit((uchar)*cp)) {
     826           0 :         if (*cp == '0') {
     827           0 :             return 0;
     828             :         }
     829           0 :         errno = 0;
     830           0 :         spec->min_width = mpd_strtossize(cp, &cp, 10);
     831           0 :         if (errno == ERANGE || errno == EINVAL) {
     832           0 :             return 0;
     833             :         }
     834             :     }
     835             : 
     836             :     /* thousands separator */
     837           0 :     if (*cp == ',') {
     838           0 :         spec->dot = ".";
     839           0 :         spec->sep = ",";
     840           0 :         spec->grouping = "\003\003";
     841           0 :         cp++;
     842             :     }
     843             : 
     844             :     /* fraction digits or significant digits */
     845           0 :     if (*cp == '.') {
     846           0 :         cp++;
     847           0 :         if (!isdigit((uchar)*cp)) {
     848           0 :             return 0;
     849             :         }
     850           0 :         errno = 0;
     851           0 :         spec->prec = mpd_strtossize(cp, &cp, 10);
     852           0 :         if (errno == ERANGE || errno == EINVAL) {
     853           0 :             return 0;
     854             :         }
     855             :     }
     856             : 
     857             :     /* type */
     858           0 :     if (*cp == 'E' || *cp == 'e' || *cp == 'F' || *cp == 'f' ||
     859           0 :         *cp == 'G' || *cp == 'g' || *cp == '%') {
     860           0 :         spec->type = *cp++;
     861             :     }
     862           0 :     else if (*cp == 'N' || *cp == 'n') {
     863             :         /* locale specific conversion */
     864             :         struct lconv *lc;
     865             :         /* separator has already been specified */
     866           0 :         if (*spec->sep) {
     867           0 :             return 0;
     868             :         }
     869           0 :         spec->type = *cp++;
     870           0 :         spec->type = (spec->type == 'N') ? 'G' : 'g';
     871           0 :         lc = localeconv();
     872           0 :         spec->dot = lc->decimal_point;
     873           0 :         spec->sep = lc->thousands_sep;
     874           0 :         spec->grouping = lc->grouping;
     875           0 :         if (mpd_validate_lconv(spec) < 0) {
     876           0 :             return 0; /* GCOV_NOT_REACHED */
     877             :         }
     878             :     }
     879             : 
     880             :     /* check correctness */
     881           0 :     if (*cp != '\0') {
     882           0 :         return 0;
     883             :     }
     884             : 
     885           0 :     return 1;
     886             : }
     887             : 
     888             : /*
     889             :  * The following functions assume that spec->min_width <= MPD_MAX_PREC, which
     890             :  * is made sure in mpd_qformat_spec. Then, even with a spec that inserts a
     891             :  * four-byte separator after each digit, nbytes in the following struct
     892             :  * cannot overflow.
     893             :  */
     894             : 
     895             : /* Multibyte string */
     896             : typedef struct {
     897             :     mpd_ssize_t nbytes; /* length in bytes */
     898             :     mpd_ssize_t nchars; /* length in chars */
     899             :     mpd_ssize_t cur;    /* current write index */
     900             :     char *data;
     901             : } mpd_mbstr_t;
     902             : 
     903             : static inline void
     904           0 : _mpd_bcopy(char *dest, const char *src, mpd_ssize_t n)
     905             : {
     906           0 :     while (--n >= 0) {
     907           0 :         dest[n] = src[n];
     908             :     }
     909           0 : }
     910             : 
     911             : static inline void
     912           0 : _mbstr_copy_char(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
     913             : {
     914           0 :     dest->nbytes += n;
     915           0 :     dest->nchars += (n > 0 ? 1 : 0);
     916           0 :     dest->cur -= n;
     917             : 
     918           0 :     if (dest->data != NULL) {
     919           0 :         _mpd_bcopy(dest->data+dest->cur, src, n);
     920             :     }
     921           0 : }
     922             : 
     923             : static inline void
     924           0 : _mbstr_copy_ascii(mpd_mbstr_t *dest, const char *src, mpd_ssize_t n)
     925             : {
     926           0 :     dest->nbytes += n;
     927           0 :     dest->nchars += n;
     928           0 :     dest->cur -= n;
     929             : 
     930           0 :     if (dest->data != NULL) {
     931           0 :         _mpd_bcopy(dest->data+dest->cur, src, n);
     932             :     }
     933           0 : }
     934             : 
     935             : static inline void
     936           0 : _mbstr_copy_pad(mpd_mbstr_t *dest, mpd_ssize_t n)
     937             : {
     938           0 :     dest->nbytes += n;
     939           0 :     dest->nchars += n;
     940           0 :     dest->cur -= n;
     941             : 
     942           0 :     if (dest->data != NULL) {
     943           0 :         char *cp = dest->data + dest->cur;
     944           0 :         while (--n >= 0) {
     945           0 :             cp[n] = '0';
     946             :         }
     947             :     }
     948           0 : }
     949             : 
     950             : /*
     951             :  * Copy a numeric string to dest->data, adding separators in the integer
     952             :  * part according to spec->grouping. If leading zero padding is enabled
     953             :  * and the result is smaller than spec->min_width, continue adding zeros
     954             :  * and separators until the minimum width is reached.
     955             :  *
     956             :  * The final length of dest->data is stored in dest->nbytes. The number
     957             :  * of UTF-8 characters is stored in dest->nchars.
     958             :  *
     959             :  * First run (dest->data == NULL): determine the length of the result
     960             :  * string and store it in dest->nbytes.
     961             :  *
     962             :  * Second run (write to dest->data): data is written in chunks and in
     963             :  * reverse order, starting with the rest of the numeric string.
     964             :  */
     965             : static void
     966           0 : _mpd_add_sep_dot(mpd_mbstr_t *dest,
     967             :                  const char *sign, /* location of optional sign */
     968             :                  const char *src, mpd_ssize_t n_src, /* integer part and length */
     969             :                  const char *dot, /* location of optional decimal point */
     970             :                  const char *rest, mpd_ssize_t n_rest, /* remaining part and length */
     971             :                  const mpd_spec_t *spec)
     972             : {
     973             :     mpd_ssize_t n_sep, n_sign, consume;
     974             :     const char *g;
     975           0 :     int pad = 0;
     976             : 
     977           0 :     n_sign = sign ? 1 : 0;
     978           0 :     n_sep = (mpd_ssize_t)strlen(spec->sep);
     979             :     /* Initial write index: set to location of '\0' in the output string.
     980             :      * Irrelevant for the first run. */
     981           0 :     dest->cur = dest->nbytes;
     982           0 :     dest->nbytes = dest->nchars = 0;
     983             : 
     984           0 :     _mbstr_copy_ascii(dest, rest, n_rest);
     985             : 
     986           0 :     if (dot) {
     987           0 :         _mbstr_copy_char(dest, dot, (mpd_ssize_t)strlen(dot));
     988             :     }
     989             : 
     990           0 :     g = spec->grouping;
     991           0 :     consume = *g;
     992             :     while (1) {
     993             :         /* If the group length is 0 or CHAR_MAX or greater than the
     994             :          * number of source bytes, consume all remaining bytes. */
     995           0 :         if (*g == 0 || *g == CHAR_MAX || consume > n_src) {
     996           0 :             consume = n_src;
     997             :         }
     998           0 :         n_src -= consume;
     999           0 :         if (pad) {
    1000           0 :             _mbstr_copy_pad(dest, consume);
    1001             :         }
    1002             :         else {
    1003           0 :             _mbstr_copy_ascii(dest, src+n_src, consume);
    1004             :         }
    1005             : 
    1006           0 :         if (n_src == 0) {
    1007             :             /* Either the real source of intpart digits or the virtual
    1008             :              * source of padding zeros is exhausted. */
    1009           0 :             if (spec->align == 'z' &&
    1010           0 :                 dest->nchars + n_sign < spec->min_width) {
    1011             :                 /* Zero padding is set and length < min_width:
    1012             :                  * Generate n_src additional characters. */
    1013           0 :                 n_src = spec->min_width - (dest->nchars + n_sign);
    1014             :                 /* Next iteration:
    1015             :                  *   case *g == 0 || *g == CHAR_MAX:
    1016             :                  *      consume all padding characters
    1017             :                  *   case consume < g*:
    1018             :                  *      fill remainder of current group
    1019             :                  *   case consume == g*
    1020             :                  *      copying is a no-op */
    1021           0 :                 consume = *g - consume;
    1022             :                 /* Switch on virtual source of zeros. */
    1023           0 :                 pad = 1;
    1024           0 :                 continue;
    1025             :             }
    1026           0 :             break;
    1027             :         }
    1028             : 
    1029           0 :         if (n_sep > 0) {
    1030             :             /* If padding is switched on, separators are counted
    1031             :              * as padding characters. This rule does not apply if
    1032             :              * the separator would be the first character of the
    1033             :              * result string. */
    1034           0 :             if (pad && n_src > 1) n_src -= 1;
    1035           0 :             _mbstr_copy_char(dest, spec->sep, n_sep);
    1036             :         }
    1037             : 
    1038             :         /* If non-NUL, use the next value for grouping. */
    1039           0 :         if (*g && *(g+1)) g++;
    1040           0 :         consume = *g;
    1041           0 :     }
    1042             : 
    1043           0 :     if (sign) {
    1044           0 :         _mbstr_copy_ascii(dest, sign, 1);
    1045             :     }
    1046             : 
    1047           0 :     if (dest->data) {
    1048           0 :         dest->data[dest->nbytes] = '\0';
    1049             :     }
    1050           0 : }
    1051             : 
    1052             : /*
    1053             :  * Convert a numeric-string to its locale-specific appearance.
    1054             :  * The string must have one of these forms:
    1055             :  *
    1056             :  *     1) [sign] digits [exponent-part]
    1057             :  *     2) [sign] digits '.' [digits] [exponent-part]
    1058             :  *
    1059             :  * Not allowed, since _mpd_to_string() never returns this form:
    1060             :  *
    1061             :  *     3) [sign] '.' digits [exponent-part]
    1062             :  * 
    1063             :  * Input: result->data := original numeric string (ASCII)
    1064             :  *        result->bytes := strlen(result->data)
    1065             :  *        result->nchars := strlen(result->data)
    1066             :  *
    1067             :  * Output: result->data := modified or original string
    1068             :  *         result->bytes := strlen(result->data)
    1069             :  *         result->nchars := number of characters (possibly UTF-8)
    1070             :  */
    1071             : static int
    1072           0 : _mpd_apply_lconv(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
    1073             : {
    1074           0 :     const char *sign = NULL, *intpart = NULL, *dot = NULL;
    1075             :     const char *rest, *dp;
    1076             :     char *decstring;
    1077             :     mpd_ssize_t n_int, n_rest;
    1078             : 
    1079             :     /* original numeric string */
    1080           0 :     dp = result->data;
    1081             : 
    1082             :     /* sign */
    1083           0 :     if (*dp == '+' || *dp == '-' || *dp == ' ') {
    1084           0 :         sign = dp++;
    1085             :     }
    1086             :     /* integer part */
    1087             :     assert(isdigit((uchar)*dp));
    1088           0 :     intpart = dp++;
    1089           0 :     while (isdigit((uchar)*dp)) {
    1090           0 :         dp++;
    1091             :     }
    1092           0 :     n_int = (mpd_ssize_t)(dp-intpart);
    1093             :     /* decimal point */
    1094           0 :     if (*dp == '.') {
    1095           0 :         dp++; dot = spec->dot;
    1096             :     }
    1097             :     /* rest */
    1098           0 :     rest = dp;
    1099           0 :     n_rest = result->nbytes - (mpd_ssize_t)(dp-result->data);
    1100             : 
    1101           0 :     if (dot == NULL && (*spec->sep == '\0' || *spec->grouping == '\0')) {
    1102             :         /* _mpd_add_sep_dot() would not change anything */
    1103           0 :         return 1;
    1104             :     }
    1105             : 
    1106             :     /* Determine the size of the new decimal string after inserting the
    1107             :      * decimal point, optional separators and optional padding. */
    1108           0 :     decstring = result->data;
    1109           0 :     result->data = NULL;
    1110           0 :     _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
    1111             :                      rest, n_rest, spec);
    1112             : 
    1113           0 :     result->data = mpd_alloc(result->nbytes+1, 1);
    1114           0 :     if (result->data == NULL) {
    1115           0 :         *status |= MPD_Malloc_error;
    1116           0 :         mpd_free(decstring);
    1117           0 :         return 0;
    1118             :     }
    1119             : 
    1120             :     /* Perform actual writes. */
    1121           0 :     _mpd_add_sep_dot(result, sign, intpart, n_int, dot,
    1122             :                      rest, n_rest, spec);
    1123             : 
    1124           0 :     mpd_free(decstring);
    1125           0 :     return 1;
    1126             : }
    1127             : 
    1128             : /* Add padding to the formatted string if necessary. */
    1129             : static int
    1130           0 : _mpd_add_pad(mpd_mbstr_t *result, const mpd_spec_t *spec, uint32_t *status)
    1131             : {
    1132           0 :     if (result->nchars < spec->min_width) {
    1133             :         mpd_ssize_t add_chars, add_bytes;
    1134           0 :         size_t lpad = 0, rpad = 0;
    1135             :         size_t n_fill, len, i, j;
    1136           0 :         char align = spec->align;
    1137           0 :         uint8_t err = 0;
    1138             :         char *cp;
    1139             : 
    1140           0 :         n_fill = strlen(spec->fill);
    1141           0 :         add_chars = (spec->min_width - result->nchars);
    1142             :         /* max value: MPD_MAX_PREC * 4 */
    1143           0 :         add_bytes = add_chars * (mpd_ssize_t)n_fill;
    1144             : 
    1145           0 :         cp = result->data = mpd_realloc(result->data,
    1146           0 :                                         result->nbytes+add_bytes+1,
    1147             :                                         sizeof *result->data, &err);
    1148           0 :         if (err) {
    1149           0 :             *status |= MPD_Malloc_error;
    1150           0 :             mpd_free(result->data);
    1151           0 :             return 0;
    1152             :         }
    1153             : 
    1154           0 :         if (align == 'z') {
    1155           0 :             align = '=';
    1156             :         }
    1157             : 
    1158           0 :         if (align == '<') {
    1159           0 :             rpad = add_chars;
    1160             :         }
    1161           0 :         else if (align == '>' || align == '=') {
    1162           0 :             lpad = add_chars;
    1163             :         }
    1164             :         else { /* align == '^' */
    1165           0 :             lpad = add_chars/2;
    1166           0 :             rpad = add_chars-lpad;
    1167             :         }
    1168             : 
    1169           0 :         len = result->nbytes;
    1170           0 :         if (align == '=' && (*cp == '-' || *cp == '+' || *cp == ' ')) {
    1171             :             /* leave sign in the leading position */
    1172           0 :             cp++; len--;
    1173             :         }
    1174             : 
    1175           0 :         memmove(cp+n_fill*lpad, cp, len);
    1176           0 :         for (i = 0; i < lpad; i++) {
    1177           0 :             for (j = 0; j < n_fill; j++) {
    1178           0 :                 cp[i*n_fill+j] = spec->fill[j];
    1179             :             }
    1180             :         }
    1181           0 :         cp += (n_fill*lpad + len);
    1182           0 :         for (i = 0; i < rpad; i++) {
    1183           0 :             for (j = 0; j < n_fill; j++) {
    1184           0 :                 cp[i*n_fill+j] = spec->fill[j];
    1185             :             }
    1186             :         }
    1187             : 
    1188           0 :         result->nbytes += add_bytes;
    1189           0 :         result->nchars += add_chars;
    1190           0 :         result->data[result->nbytes] = '\0';
    1191             :     }
    1192             : 
    1193           0 :     return 1;
    1194             : }
    1195             : 
    1196             : /* Round a number to prec digits. The adjusted exponent stays the same
    1197             :    or increases by one if rounding up crosses a power of ten boundary.
    1198             :    If result->digits would exceed MPD_MAX_PREC+1, MPD_Invalid_operation
    1199             :    is set and the result is NaN. */
    1200             : static inline void
    1201           0 : _mpd_round(mpd_t *result, const mpd_t *a, mpd_ssize_t prec,
    1202             :            const mpd_context_t *ctx, uint32_t *status)
    1203             : {
    1204           0 :     mpd_ssize_t exp = a->exp + a->digits - prec;
    1205             : 
    1206           0 :     if (prec <= 0) {
    1207           0 :         mpd_seterror(result, MPD_Invalid_operation, status); /* GCOV_NOT_REACHED */
    1208           0 :         return; /* GCOV_NOT_REACHED */
    1209             :     }
    1210           0 :     if (mpd_isspecial(a) || mpd_iszero(a)) {
    1211           0 :         mpd_qcopy(result, a, status); /* GCOV_NOT_REACHED */
    1212           0 :         return; /* GCOV_NOT_REACHED */
    1213             :     }
    1214             : 
    1215           0 :     mpd_qrescale_fmt(result, a, exp, ctx, status);
    1216           0 :     if (result->digits > prec) {
    1217           0 :         mpd_qrescale_fmt(result, result, exp+1, ctx, status);
    1218             :     }
    1219             : }
    1220             : 
    1221             : /*
    1222             :  * Return the string representation of an mpd_t, formatted according to 'spec'.
    1223             :  * The format specification is assumed to be valid. Memory errors are indicated
    1224             :  * as usual. This function is quiet.
    1225             :  */
    1226             : char *
    1227           0 : mpd_qformat_spec(const mpd_t *dec, const mpd_spec_t *spec,
    1228             :                  const mpd_context_t *ctx, uint32_t *status)
    1229             : {
    1230             :     mpd_uint_t dt[MPD_MINALLOC_MAX];
    1231           0 :     mpd_t tmp = {MPD_STATIC|MPD_STATIC_DATA,0,0,0,MPD_MINALLOC_MAX,dt};
    1232           0 :     mpd_ssize_t dplace = MPD_DEFAULT_DOTPLACE;
    1233             :     mpd_mbstr_t result;
    1234             :     mpd_spec_t stackspec;
    1235           0 :     char type = spec->type;
    1236           0 :     int flags = 0;
    1237             : 
    1238             : 
    1239           0 :     if (spec->min_width > MPD_MAX_PREC) {
    1240           0 :         *status |= MPD_Invalid_operation;
    1241           0 :         return NULL;
    1242             :     }
    1243             : 
    1244           0 :     if (isupper((uchar)type)) {
    1245           0 :         type = tolower((uchar)type);
    1246           0 :         flags |= MPD_FMT_UPPER;
    1247             :     }
    1248           0 :     if (spec->sign == ' ') {
    1249           0 :         flags |= MPD_FMT_SIGN_SPACE;
    1250             :     }
    1251           0 :     else if (spec->sign == '+') {
    1252           0 :         flags |= MPD_FMT_SIGN_PLUS;
    1253             :     }
    1254             : 
    1255           0 :     if (mpd_isspecial(dec)) {
    1256           0 :         if (spec->align == 'z') {
    1257           0 :             stackspec = *spec;
    1258           0 :             stackspec.fill[0] = ' ';
    1259           0 :             stackspec.fill[1] = '\0';
    1260           0 :             stackspec.align = '>';
    1261           0 :             spec = &stackspec;
    1262             :         }
    1263             :     }
    1264             :     else {
    1265           0 :         uint32_t workstatus = 0;
    1266             :         mpd_ssize_t prec;
    1267             : 
    1268           0 :         switch (type) {
    1269           0 :         case 'g': flags |= MPD_FMT_TOSCI; break;
    1270           0 :         case 'e': flags |= MPD_FMT_EXP; break;
    1271           0 :         case '%': flags |= MPD_FMT_PERCENT;
    1272           0 :                   if (!mpd_qcopy(&tmp, dec, status)) {
    1273           0 :                       return NULL;
    1274             :                   }
    1275           0 :                   tmp.exp += 2;
    1276           0 :                   dec = &tmp;
    1277           0 :                   type = 'f'; /* fall through */
    1278           0 :         case 'f': flags |= MPD_FMT_FIXED; break;
    1279           0 :         default: abort(); /* debug: GCOV_NOT_REACHED */
    1280             :         }
    1281             : 
    1282           0 :         if (spec->prec >= 0) {
    1283           0 :             if (spec->prec > MPD_MAX_PREC) {
    1284           0 :                 *status |= MPD_Invalid_operation;
    1285             :                 goto error;
    1286             :             }
    1287             : 
    1288           0 :             switch (type) {
    1289             :             case 'g':
    1290           0 :                 prec = (spec->prec == 0) ? 1 : spec->prec;
    1291           0 :                 if (dec->digits > prec) {
    1292           0 :                     _mpd_round(&tmp, dec, prec, ctx,
    1293             :                                &workstatus);
    1294           0 :                     dec = &tmp;
    1295             :                 }
    1296           0 :                 break;
    1297             :             case 'e':
    1298           0 :                 if (mpd_iszero(dec)) {
    1299           0 :                     dplace = 1-spec->prec;
    1300             :                 }
    1301             :                 else {
    1302           0 :                     _mpd_round(&tmp, dec, spec->prec+1, ctx,
    1303             :                                &workstatus);
    1304           0 :                     dec = &tmp;
    1305             :                 }
    1306           0 :                 break;
    1307             :             case 'f':
    1308           0 :                 mpd_qrescale(&tmp, dec, -spec->prec, ctx,
    1309             :                              &workstatus);
    1310           0 :                 dec = &tmp;
    1311           0 :                 break;
    1312             :             }
    1313             :         }
    1314             : 
    1315           0 :         if (type == 'f') {
    1316           0 :             if (mpd_iszero(dec) && dec->exp > 0) {
    1317           0 :                 mpd_qrescale(&tmp, dec, 0, ctx, &workstatus);
    1318           0 :                 dec = &tmp;
    1319             :             }
    1320             :         }
    1321             : 
    1322           0 :         if (workstatus&MPD_Errors) {
    1323           0 :             *status |= (workstatus&MPD_Errors);
    1324             :             goto error;
    1325             :         }
    1326             :     }
    1327             : 
    1328             :     /*
    1329             :      * At this point, for all scaled or non-scaled decimals:
    1330             :      *   1) 1 <= digits <= MAX_PREC+1
    1331             :      *   2) adjexp(scaled) = adjexp(orig) [+1]
    1332             :      *   3)   case 'g': MIN_ETINY <= exp <= MAX_EMAX+1
    1333             :      *        case 'e': MIN_ETINY-MAX_PREC <= exp <= MAX_EMAX+1
    1334             :      *        case 'f': MIN_ETINY <= exp <= MAX_EMAX+1
    1335             :      *   4) max memory alloc in _mpd_to_string:
    1336             :      *        case 'g': MAX_PREC+36
    1337             :      *        case 'e': MAX_PREC+36
    1338             :      *        case 'f': 2*MPD_MAX_PREC+30
    1339             :      */
    1340           0 :     result.nbytes = _mpd_to_string(&result.data, dec, flags, dplace);
    1341           0 :     result.nchars = result.nbytes;
    1342           0 :     if (result.nbytes < 0) {
    1343           0 :         *status |= MPD_Malloc_error;
    1344           0 :         goto error;
    1345             :     }
    1346             : 
    1347           0 :     if (*spec->dot != '\0' && !mpd_isspecial(dec)) {
    1348           0 :         if (result.nchars > MPD_MAX_PREC+36) {
    1349             :             /* Since a group length of one is not explicitly
    1350             :              * disallowed, ensure that it is always possible to
    1351             :              * insert a four byte separator after each digit. */
    1352           0 :             *status |= MPD_Invalid_operation;
    1353           0 :             mpd_free(result.data);
    1354           0 :             goto error;
    1355             :         }
    1356           0 :         if (!_mpd_apply_lconv(&result, spec, status)) {
    1357           0 :             goto error;
    1358             :         }
    1359             :     }
    1360             : 
    1361           0 :     if (spec->min_width) {
    1362           0 :         if (!_mpd_add_pad(&result, spec, status)) {
    1363           0 :             goto error;
    1364             :         }
    1365             :     }
    1366             : 
    1367           0 :     mpd_del(&tmp);
    1368           0 :     return result.data;
    1369             : 
    1370             : error:
    1371           0 :     mpd_del(&tmp);
    1372           0 :     return NULL;
    1373             : }
    1374             : 
    1375             : char *
    1376           0 : mpd_qformat(const mpd_t *dec, const char *fmt, const mpd_context_t *ctx,
    1377             :             uint32_t *status)
    1378             : {
    1379             :     mpd_spec_t spec;
    1380             : 
    1381           0 :     if (!mpd_parse_fmt_str(&spec, fmt, 1)) {
    1382           0 :         *status |= MPD_Invalid_operation;
    1383           0 :         return NULL;
    1384             :     }
    1385             : 
    1386           0 :     return mpd_qformat_spec(dec, &spec, ctx, status);
    1387             : }
    1388             : 
    1389             : /*
    1390             :  * The specification has a *condition* called Invalid_operation and an
    1391             :  * IEEE *signal* called Invalid_operation. The former corresponds to
    1392             :  * MPD_Invalid_operation, the latter to MPD_IEEE_Invalid_operation.
    1393             :  * MPD_IEEE_Invalid_operation comprises the following conditions:
    1394             :  *
    1395             :  * [MPD_Conversion_syntax, MPD_Division_impossible, MPD_Division_undefined,
    1396             :  *  MPD_Fpu_error, MPD_Invalid_context, MPD_Invalid_operation,
    1397             :  *  MPD_Malloc_error]
    1398             :  *
    1399             :  * In the following functions, 'flag' denotes the condition, 'signal'
    1400             :  * denotes the IEEE signal.
    1401             :  */
    1402             : 
    1403             : static const char *mpd_flag_string[MPD_NUM_FLAGS] = {
    1404             :     "Clamped",
    1405             :     "Conversion_syntax",
    1406             :     "Division_by_zero",
    1407             :     "Division_impossible",
    1408             :     "Division_undefined",
    1409             :     "Fpu_error",
    1410             :     "Inexact",
    1411             :     "Invalid_context",
    1412             :     "Invalid_operation",
    1413             :     "Malloc_error",
    1414             :     "Not_implemented",
    1415             :     "Overflow",
    1416             :     "Rounded",
    1417             :     "Subnormal",
    1418             :     "Underflow",
    1419             : };
    1420             : 
    1421             : static const char *mpd_signal_string[MPD_NUM_FLAGS] = {
    1422             :     "Clamped",
    1423             :     "IEEE_Invalid_operation",
    1424             :     "Division_by_zero",
    1425             :     "IEEE_Invalid_operation",
    1426             :     "IEEE_Invalid_operation",
    1427             :     "IEEE_Invalid_operation",
    1428             :     "Inexact",
    1429             :     "IEEE_Invalid_operation",
    1430             :     "IEEE_Invalid_operation",
    1431             :     "IEEE_Invalid_operation",
    1432             :     "Not_implemented",
    1433             :     "Overflow",
    1434             :     "Rounded",
    1435             :     "Subnormal",
    1436             :     "Underflow",
    1437             : };
    1438             : 
    1439             : /* print conditions to buffer, separated by spaces */
    1440             : int
    1441           0 : mpd_snprint_flags(char *dest, int nmemb, uint32_t flags)
    1442             : {
    1443             :     char *cp;
    1444             :     int n, j;
    1445             : 
    1446             :     assert(nmemb >= MPD_MAX_FLAG_STRING);
    1447             : 
    1448           0 :     *dest = '\0'; cp = dest;
    1449           0 :     for (j = 0; j < MPD_NUM_FLAGS; j++) {
    1450           0 :         if (flags & (1U<<j)) {
    1451           0 :             n = snprintf(cp, nmemb, "%s ", mpd_flag_string[j]);
    1452           0 :             if (n < 0 || n >= nmemb) return -1;
    1453           0 :             cp += n; nmemb -= n;
    1454             :         }
    1455             :     }
    1456             : 
    1457           0 :     if (cp != dest) {
    1458           0 :         *(--cp) = '\0';
    1459             :     }
    1460             : 
    1461           0 :     return (int)(cp-dest);
    1462             : }
    1463             : 
    1464             : /* print conditions to buffer, in list form */
    1465             : int
    1466           0 : mpd_lsnprint_flags(char *dest, int nmemb, uint32_t flags, const char *flag_string[])
    1467             : {
    1468             :     char *cp;
    1469             :     int n, j;
    1470             : 
    1471             :     assert(nmemb >= MPD_MAX_FLAG_LIST);
    1472           0 :     if (flag_string == NULL) {
    1473           0 :         flag_string = mpd_flag_string;
    1474             :     }
    1475             : 
    1476           0 :     *dest = '[';
    1477           0 :     *(dest+1) = '\0';
    1478           0 :     cp = dest+1;
    1479           0 :     --nmemb;
    1480             : 
    1481           0 :     for (j = 0; j < MPD_NUM_FLAGS; j++) {
    1482           0 :         if (flags & (1U<<j)) {
    1483           0 :             n = snprintf(cp, nmemb, "%s, ", flag_string[j]);
    1484           0 :             if (n < 0 || n >= nmemb) return -1;
    1485           0 :             cp += n; nmemb -= n;
    1486             :         }
    1487             :     }
    1488             : 
    1489             :     /* erase the last ", " */
    1490           0 :     if (cp != dest+1) {
    1491           0 :         cp -= 2;
    1492             :     }
    1493             : 
    1494           0 :     *cp++ = ']';
    1495           0 :     *cp = '\0';
    1496             : 
    1497           0 :     return (int)(cp-dest); /* strlen, without NUL terminator */
    1498             : }
    1499             : 
    1500             : /* print signals to buffer, in list form */
    1501             : int
    1502           0 : mpd_lsnprint_signals(char *dest, int nmemb, uint32_t flags, const char *signal_string[])
    1503             : {
    1504             :     char *cp;
    1505             :     int n, j;
    1506           0 :     int ieee_invalid_done = 0;
    1507             : 
    1508             :     assert(nmemb >= MPD_MAX_SIGNAL_LIST);
    1509           0 :     if (signal_string == NULL) {
    1510           0 :         signal_string = mpd_signal_string;
    1511             :     }
    1512             : 
    1513           0 :     *dest = '[';
    1514           0 :     *(dest+1) = '\0';
    1515           0 :     cp = dest+1;
    1516           0 :     --nmemb;
    1517             : 
    1518           0 :     for (j = 0; j < MPD_NUM_FLAGS; j++) {
    1519           0 :         uint32_t f = flags & (1U<<j);
    1520           0 :         if (f) {
    1521           0 :             if (f&MPD_IEEE_Invalid_operation) {
    1522           0 :                 if (ieee_invalid_done) {
    1523           0 :                     continue;
    1524             :                 }
    1525           0 :                 ieee_invalid_done = 1;
    1526             :             }
    1527           0 :             n = snprintf(cp, nmemb, "%s, ", signal_string[j]);
    1528           0 :             if (n < 0 || n >= nmemb) return -1;
    1529           0 :             cp += n; nmemb -= n;
    1530             :         }
    1531             :     }
    1532             : 
    1533             :     /* erase the last ", " */
    1534           0 :     if (cp != dest+1) {
    1535           0 :         cp -= 2;
    1536             :     }
    1537             : 
    1538           0 :     *cp++ = ']';
    1539           0 :     *cp = '\0';
    1540             : 
    1541           0 :     return (int)(cp-dest); /* strlen, without NUL terminator */
    1542             : }
    1543             : 
    1544             : /* The following two functions are mainly intended for debugging. */
    1545             : void
    1546           0 : mpd_fprint(FILE *file, const mpd_t *dec)
    1547             : {
    1548             :     char *decstring;
    1549             : 
    1550           0 :     decstring = mpd_to_sci(dec, 1);
    1551           0 :     if (decstring != NULL) {
    1552           0 :         fprintf(file, "%s\n", decstring);
    1553           0 :         mpd_free(decstring);
    1554             :     }
    1555             :     else {
    1556           0 :         fputs("mpd_fprint: output error\n", file); /* GCOV_NOT_REACHED */
    1557             :     }
    1558           0 : }
    1559             : 
    1560             : void
    1561           0 : mpd_print(const mpd_t *dec)
    1562             : {
    1563             :     char *decstring;
    1564             : 
    1565           0 :     decstring = mpd_to_sci(dec, 1);
    1566           0 :     if (decstring != NULL) {
    1567           0 :         printf("%s\n", decstring);
    1568           0 :         mpd_free(decstring);
    1569             :     }
    1570             :     else {
    1571           0 :         fputs("mpd_fprint: output error\n", stderr); /* GCOV_NOT_REACHED */
    1572             :     }
    1573           0 : }
    1574             : 
    1575             : 

Generated by: LCOV version 1.10