ยปCore Development>Code coverage>Modules/_decimal/libmpdec/io.c

Python code coverage for Modules/_decimal/libmpdec/io.c

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