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 :
|