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