17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7257d1b4Sraf * Common Development and Distribution License (the "License"). 6*7257d1b4Sraf * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 21*7257d1b4Sraf 227c478bd9Sstevel@tonic-gate /* 23*7257d1b4Sraf * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 247c478bd9Sstevel@tonic-gate * Use is subject to license terms. 257c478bd9Sstevel@tonic-gate */ 267c478bd9Sstevel@tonic-gate 277c478bd9Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */ 287c478bd9Sstevel@tonic-gate /* All Rights Reserved */ 297c478bd9Sstevel@tonic-gate 30*7257d1b4Sraf #pragma ident "%Z%%M% %I% %E% SMI" 317c478bd9Sstevel@tonic-gate 327c478bd9Sstevel@tonic-gate /* 337c478bd9Sstevel@tonic-gate * This file is based on /usr/src/lib/libc/port/gen/strtod.c and 347c478bd9Sstevel@tonic-gate * /usr/src/lib/libc/sparc/fp/string_decim.c 357c478bd9Sstevel@tonic-gate */ 367c478bd9Sstevel@tonic-gate 37*7257d1b4Sraf #pragma weak _wcstod = wcstod 38*7257d1b4Sraf #pragma weak _wstod = wstod 397c478bd9Sstevel@tonic-gate 40*7257d1b4Sraf #include "lint.h" 417c478bd9Sstevel@tonic-gate #include <errno.h> 427c478bd9Sstevel@tonic-gate #include <stdio.h> 437c478bd9Sstevel@tonic-gate #include <values.h> 447c478bd9Sstevel@tonic-gate #include <floatingpoint.h> 457c478bd9Sstevel@tonic-gate #include <stddef.h> 467c478bd9Sstevel@tonic-gate #include <wctype.h> 477c478bd9Sstevel@tonic-gate #include "base_conversion.h" /* from usr/src/lib/libc/inc */ 487c478bd9Sstevel@tonic-gate #include <locale.h> 497c478bd9Sstevel@tonic-gate #include "libc.h" 507c478bd9Sstevel@tonic-gate #include "xpg6.h" 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate static void wstring_to_decimal(const wchar_t **, int, decimal_record *, int *); 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate double 55*7257d1b4Sraf wcstod(const wchar_t *cp, wchar_t **ptr) 567c478bd9Sstevel@tonic-gate { 577c478bd9Sstevel@tonic-gate double x; 587c478bd9Sstevel@tonic-gate decimal_mode mr; 597c478bd9Sstevel@tonic-gate decimal_record dr; 607c478bd9Sstevel@tonic-gate fp_exception_field_type fs; 617c478bd9Sstevel@tonic-gate int form; 627c478bd9Sstevel@tonic-gate 637c478bd9Sstevel@tonic-gate wstring_to_decimal(&cp, __xpg6 & _C99SUSv3_recognize_hexfp, &dr, &form); 647c478bd9Sstevel@tonic-gate if (ptr != NULL) 657c478bd9Sstevel@tonic-gate *ptr = (wchar_t *)cp; 667c478bd9Sstevel@tonic-gate if (form == 0) 677c478bd9Sstevel@tonic-gate return (0.0); /* Shameful kluge for SVID's sake. */ 687c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 697c478bd9Sstevel@tonic-gate mr.rd = __xgetRD(); 707c478bd9Sstevel@tonic-gate #elif defined(__sparc) 717c478bd9Sstevel@tonic-gate mr.rd = _QgetRD(); 727c478bd9Sstevel@tonic-gate #else 737c478bd9Sstevel@tonic-gate #error Unknown architecture! 747c478bd9Sstevel@tonic-gate #endif 757c478bd9Sstevel@tonic-gate if (form < 0) 767c478bd9Sstevel@tonic-gate __hex_to_double(&dr, mr.rd, &x, &fs); 777c478bd9Sstevel@tonic-gate else 787c478bd9Sstevel@tonic-gate decimal_to_double(&x, &mr, &dr, &fs); 797c478bd9Sstevel@tonic-gate if (fs & ((1 << fp_overflow) | (1 << fp_underflow))) 807c478bd9Sstevel@tonic-gate errno = ERANGE; 817c478bd9Sstevel@tonic-gate return (x); 827c478bd9Sstevel@tonic-gate } 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate float 857c478bd9Sstevel@tonic-gate wcstof(const wchar_t *cp, wchar_t **ptr) 867c478bd9Sstevel@tonic-gate { 877c478bd9Sstevel@tonic-gate float x; 887c478bd9Sstevel@tonic-gate decimal_mode mr; 897c478bd9Sstevel@tonic-gate decimal_record dr; 907c478bd9Sstevel@tonic-gate fp_exception_field_type fs; 917c478bd9Sstevel@tonic-gate int form; 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate wstring_to_decimal(&cp, 1, &dr, &form); 947c478bd9Sstevel@tonic-gate if (ptr != NULL) 957c478bd9Sstevel@tonic-gate *ptr = (wchar_t *)cp; 967c478bd9Sstevel@tonic-gate if (form == 0) 977c478bd9Sstevel@tonic-gate return (0.0f); 987c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 997c478bd9Sstevel@tonic-gate mr.rd = __xgetRD(); 1007c478bd9Sstevel@tonic-gate #elif defined(__sparc) 1017c478bd9Sstevel@tonic-gate mr.rd = _QgetRD(); 1027c478bd9Sstevel@tonic-gate #else 1037c478bd9Sstevel@tonic-gate #error Unknown architecture! 1047c478bd9Sstevel@tonic-gate #endif 1057c478bd9Sstevel@tonic-gate if (form < 0) 1067c478bd9Sstevel@tonic-gate __hex_to_single(&dr, mr.rd, &x, &fs); 1077c478bd9Sstevel@tonic-gate else 1087c478bd9Sstevel@tonic-gate decimal_to_single(&x, &mr, &dr, &fs); 1097c478bd9Sstevel@tonic-gate if (fs & ((1 << fp_overflow) | (1 << fp_underflow))) 1107c478bd9Sstevel@tonic-gate errno = ERANGE; 1117c478bd9Sstevel@tonic-gate return (x); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate long double 1157c478bd9Sstevel@tonic-gate wcstold(const wchar_t *cp, wchar_t **ptr) 1167c478bd9Sstevel@tonic-gate { 1177c478bd9Sstevel@tonic-gate long double x; 1187c478bd9Sstevel@tonic-gate decimal_mode mr; 1197c478bd9Sstevel@tonic-gate decimal_record dr; 1207c478bd9Sstevel@tonic-gate fp_exception_field_type fs; 1217c478bd9Sstevel@tonic-gate int form; 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate wstring_to_decimal(&cp, 1, &dr, &form); 1247c478bd9Sstevel@tonic-gate if (ptr != NULL) 1257c478bd9Sstevel@tonic-gate *ptr = (wchar_t *)cp; 1267c478bd9Sstevel@tonic-gate if (form == 0) 1277c478bd9Sstevel@tonic-gate return (0.0L); 1287c478bd9Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 1297c478bd9Sstevel@tonic-gate mr.rd = __xgetRD(); 1307c478bd9Sstevel@tonic-gate if (form < 0) 1317c478bd9Sstevel@tonic-gate __hex_to_extended(&dr, mr.rd, (extended *)&x, &fs); 1327c478bd9Sstevel@tonic-gate else 1337c478bd9Sstevel@tonic-gate decimal_to_extended((extended *)&x, &mr, &dr, &fs); 1347c478bd9Sstevel@tonic-gate #elif defined(__sparc) 1357c478bd9Sstevel@tonic-gate mr.rd = _QgetRD(); 1367c478bd9Sstevel@tonic-gate if (form < 0) 1377c478bd9Sstevel@tonic-gate __hex_to_quadruple(&dr, mr.rd, &x, &fs); 1387c478bd9Sstevel@tonic-gate else 1397c478bd9Sstevel@tonic-gate decimal_to_quadruple(&x, &mr, &dr, &fs); 1407c478bd9Sstevel@tonic-gate #else 1417c478bd9Sstevel@tonic-gate #error Unknown architecture! 1427c478bd9Sstevel@tonic-gate #endif 1437c478bd9Sstevel@tonic-gate if (fs & ((1 << fp_overflow) | (1 << fp_underflow))) 1447c478bd9Sstevel@tonic-gate errno = ERANGE; 1457c478bd9Sstevel@tonic-gate return (x); 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate double 149*7257d1b4Sraf wstod(const wchar_t *cp, wchar_t **ptr) 1507c478bd9Sstevel@tonic-gate { 151*7257d1b4Sraf return (wcstod(cp, ptr)); 1527c478bd9Sstevel@tonic-gate } 1537c478bd9Sstevel@tonic-gate 1547c478bd9Sstevel@tonic-gate static const char *infstring = "INFINITY"; 1557c478bd9Sstevel@tonic-gate static const char *nanstring = "NAN"; 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate /* 1587c478bd9Sstevel@tonic-gate * The following macro is applied to wchar_t arguments solely for the 1597c478bd9Sstevel@tonic-gate * purpose of comparing the result with one of the characters in the 1607c478bd9Sstevel@tonic-gate * strings above. 1617c478bd9Sstevel@tonic-gate */ 1627c478bd9Sstevel@tonic-gate #define UCASE(c) (((L'a' <= c) && (c <= L'z'))? c - 32 : c) 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate /* 1657c478bd9Sstevel@tonic-gate * The following macro yields an expression that is true whenever 1667c478bd9Sstevel@tonic-gate * the argument is a valid nonzero digit for the form being parsed. 1677c478bd9Sstevel@tonic-gate */ 1687c478bd9Sstevel@tonic-gate #define NZDIGIT(c) ((L'1' <= c && c <= L'9') || (form < 0 && \ 1697c478bd9Sstevel@tonic-gate ((L'a' <= c && c <= L'f') || (L'A' <= c && c <= L'F')))) 1707c478bd9Sstevel@tonic-gate 1717c478bd9Sstevel@tonic-gate /* 1727c478bd9Sstevel@tonic-gate * wstring_to_decimal is modelled on string_to_decimal, the majority 1737c478bd9Sstevel@tonic-gate * of which can be found in the common file char_to_decimal.h. The 1747c478bd9Sstevel@tonic-gate * significant differences are: 1757c478bd9Sstevel@tonic-gate * 1767c478bd9Sstevel@tonic-gate * 1. This code recognizes only C99 (hex fp strings and restricted 1777c478bd9Sstevel@tonic-gate * characters in parentheses following "nan") vs. C90 modes, no 1787c478bd9Sstevel@tonic-gate * Fortran conventions. 1797c478bd9Sstevel@tonic-gate * 1807c478bd9Sstevel@tonic-gate * 2. *pform is an int rather than an enum decimal_string_form. On 1817c478bd9Sstevel@tonic-gate * return, *pform == 0 if no valid token was found, *pform < 0 1827c478bd9Sstevel@tonic-gate * if a C99 hex fp string was found, and *pform > 0 if a decimal 1837c478bd9Sstevel@tonic-gate * string was found. 1847c478bd9Sstevel@tonic-gate */ 1857c478bd9Sstevel@tonic-gate static void 1867c478bd9Sstevel@tonic-gate wstring_to_decimal(const wchar_t **ppc, int c99, decimal_record *pd, 1877c478bd9Sstevel@tonic-gate int *pform) 1887c478bd9Sstevel@tonic-gate { 1897c478bd9Sstevel@tonic-gate const wchar_t *cp = *ppc; /* last character seen */ 1907c478bd9Sstevel@tonic-gate const wchar_t *good = cp - 1; /* last character accepted */ 1917c478bd9Sstevel@tonic-gate wchar_t current; /* always equal to *cp */ 1927c478bd9Sstevel@tonic-gate int sigfound; 1937c478bd9Sstevel@tonic-gate int ids = 0; 1947c478bd9Sstevel@tonic-gate int i, agree; 1957c478bd9Sstevel@tonic-gate int nzbp = 0; /* number of zeros before point */ 1967c478bd9Sstevel@tonic-gate int nzap = 0; /* number of zeros after point */ 1977c478bd9Sstevel@tonic-gate char decpt; 1987c478bd9Sstevel@tonic-gate int nfast, nfastlimit; 1997c478bd9Sstevel@tonic-gate char *pfast; 2007c478bd9Sstevel@tonic-gate int e, esign; 2017c478bd9Sstevel@tonic-gate int expshift = 0; 2027c478bd9Sstevel@tonic-gate int form; 2037c478bd9Sstevel@tonic-gate 2047c478bd9Sstevel@tonic-gate /* 2057c478bd9Sstevel@tonic-gate * This routine assumes that the radix point is a single 2067c478bd9Sstevel@tonic-gate * ASCII character, so that following this assignment, the 2077c478bd9Sstevel@tonic-gate * condition (current == decpt) will correctly detect it. 2087c478bd9Sstevel@tonic-gate */ 2097c478bd9Sstevel@tonic-gate decpt = *(localeconv()->decimal_point); 2107c478bd9Sstevel@tonic-gate 2117c478bd9Sstevel@tonic-gate /* input is invalid until we find something */ 2127c478bd9Sstevel@tonic-gate pd->fpclass = fp_signaling; 2137c478bd9Sstevel@tonic-gate pd->sign = 0; 2147c478bd9Sstevel@tonic-gate pd->exponent = 0; 2157c478bd9Sstevel@tonic-gate pd->ds[0] = '\0'; 2167c478bd9Sstevel@tonic-gate pd->more = 0; 2177c478bd9Sstevel@tonic-gate pd->ndigits = 0; 2187c478bd9Sstevel@tonic-gate *pform = form = 0; 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate /* skip white space */ 2217c478bd9Sstevel@tonic-gate current = *cp; 2227c478bd9Sstevel@tonic-gate while (iswspace((wint_t)current)) 2237c478bd9Sstevel@tonic-gate current = *++cp; 2247c478bd9Sstevel@tonic-gate 2257c478bd9Sstevel@tonic-gate /* look for optional leading sign */ 2267c478bd9Sstevel@tonic-gate if (current == L'+') { 2277c478bd9Sstevel@tonic-gate current = *++cp; 2287c478bd9Sstevel@tonic-gate } else if (current == L'-') { 2297c478bd9Sstevel@tonic-gate pd->sign = 1; 2307c478bd9Sstevel@tonic-gate current = *++cp; 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate sigfound = -1; /* -1 = no digits found yet */ 2347c478bd9Sstevel@tonic-gate 2357c478bd9Sstevel@tonic-gate /* 2367c478bd9Sstevel@tonic-gate * Admissible first non-white-space, non-sign characters are 2377c478bd9Sstevel@tonic-gate * 0-9, i, I, n, N, or the radix point. 2387c478bd9Sstevel@tonic-gate */ 2397c478bd9Sstevel@tonic-gate if (L'1' <= current && current <= L'9') { 2407c478bd9Sstevel@tonic-gate pd->fpclass = fp_normal; 2417c478bd9Sstevel@tonic-gate form = 1; 2427c478bd9Sstevel@tonic-gate good = cp; 2437c478bd9Sstevel@tonic-gate sigfound = 1; /* 1 = significant digits found */ 2447c478bd9Sstevel@tonic-gate pd->ds[ids++] = (char)current; 2457c478bd9Sstevel@tonic-gate current = *++cp; 2467c478bd9Sstevel@tonic-gate } else { 2477c478bd9Sstevel@tonic-gate switch (current) { 2487c478bd9Sstevel@tonic-gate case L'0': 2497c478bd9Sstevel@tonic-gate /* 2507c478bd9Sstevel@tonic-gate * Accept the leading zero and set pd->fpclass 2517c478bd9Sstevel@tonic-gate * accordingly, but don't set sigfound until we 2527c478bd9Sstevel@tonic-gate * determine that this isn't a "fake" hex string 2537c478bd9Sstevel@tonic-gate * (i.e., 0x.p...). 2547c478bd9Sstevel@tonic-gate */ 2557c478bd9Sstevel@tonic-gate good = cp; 2567c478bd9Sstevel@tonic-gate pd->fpclass = fp_zero; 2577c478bd9Sstevel@tonic-gate if (c99) { 2587c478bd9Sstevel@tonic-gate /* look for a hex fp string */ 2597c478bd9Sstevel@tonic-gate current = *++cp; 2607c478bd9Sstevel@tonic-gate if (current == L'X' || current == L'x') { 2617c478bd9Sstevel@tonic-gate /* assume hex fp form */ 2627c478bd9Sstevel@tonic-gate form = -1; 2637c478bd9Sstevel@tonic-gate expshift = 2; 2647c478bd9Sstevel@tonic-gate current = *++cp; 2657c478bd9Sstevel@tonic-gate /* 2667c478bd9Sstevel@tonic-gate * Only a digit or radix point can 2677c478bd9Sstevel@tonic-gate * follow "0x". 2687c478bd9Sstevel@tonic-gate */ 2697c478bd9Sstevel@tonic-gate if (NZDIGIT(current)) { 2707c478bd9Sstevel@tonic-gate pd->fpclass = fp_normal; 2717c478bd9Sstevel@tonic-gate good = cp; 2727c478bd9Sstevel@tonic-gate sigfound = 1; 2737c478bd9Sstevel@tonic-gate pd->ds[ids++] = (char)current; 2747c478bd9Sstevel@tonic-gate current = *++cp; 2757c478bd9Sstevel@tonic-gate break; 2767c478bd9Sstevel@tonic-gate } else if (current == (wchar_t)decpt) { 2777c478bd9Sstevel@tonic-gate current = *++cp; 2787c478bd9Sstevel@tonic-gate goto afterpoint; 2797c478bd9Sstevel@tonic-gate } else if (current != L'0') { 2807c478bd9Sstevel@tonic-gate /* not hex fp after all */ 2817c478bd9Sstevel@tonic-gate form = 1; 2827c478bd9Sstevel@tonic-gate expshift = 0; 2837c478bd9Sstevel@tonic-gate goto done; 2847c478bd9Sstevel@tonic-gate } 2857c478bd9Sstevel@tonic-gate } else { 2867c478bd9Sstevel@tonic-gate form = 1; 2877c478bd9Sstevel@tonic-gate } 2887c478bd9Sstevel@tonic-gate } else { 2897c478bd9Sstevel@tonic-gate form = 1; 2907c478bd9Sstevel@tonic-gate } 2917c478bd9Sstevel@tonic-gate 2927c478bd9Sstevel@tonic-gate /* skip all leading zeros */ 2937c478bd9Sstevel@tonic-gate while (current == L'0') 2947c478bd9Sstevel@tonic-gate current = *++cp; 2957c478bd9Sstevel@tonic-gate good = cp - 1; 2967c478bd9Sstevel@tonic-gate sigfound = 0; /* 0 = only zeros found so far */ 2977c478bd9Sstevel@tonic-gate break; 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate case L'i': 3007c478bd9Sstevel@tonic-gate case L'I': 3017c478bd9Sstevel@tonic-gate /* look for inf or infinity */ 3027c478bd9Sstevel@tonic-gate current = *++cp; 3037c478bd9Sstevel@tonic-gate agree = 1; 3047c478bd9Sstevel@tonic-gate while (agree <= 7 && 3057c478bd9Sstevel@tonic-gate UCASE(current) == (wchar_t)infstring[agree]) { 3067c478bd9Sstevel@tonic-gate current = *++cp; 3077c478bd9Sstevel@tonic-gate agree++; 3087c478bd9Sstevel@tonic-gate } 3097c478bd9Sstevel@tonic-gate if (agree >= 3) { 3107c478bd9Sstevel@tonic-gate /* found valid infinity */ 3117c478bd9Sstevel@tonic-gate pd->fpclass = fp_infinity; 3127c478bd9Sstevel@tonic-gate form = 1; 3137c478bd9Sstevel@tonic-gate good = (agree < 8)? cp + 2 - agree : cp - 1; 3147c478bd9Sstevel@tonic-gate __inf_read = 1; 3157c478bd9Sstevel@tonic-gate } 3167c478bd9Sstevel@tonic-gate goto done; 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate case L'n': 3197c478bd9Sstevel@tonic-gate case L'N': 3207c478bd9Sstevel@tonic-gate /* look for nan or nan(string) */ 3217c478bd9Sstevel@tonic-gate current = *++cp; 3227c478bd9Sstevel@tonic-gate agree = 1; 3237c478bd9Sstevel@tonic-gate while (agree <= 2 && 3247c478bd9Sstevel@tonic-gate UCASE(current) == (wchar_t)nanstring[agree]) { 3257c478bd9Sstevel@tonic-gate current = *++cp; 3267c478bd9Sstevel@tonic-gate agree++; 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate if (agree == 3) { 3297c478bd9Sstevel@tonic-gate /* found valid NaN */ 3307c478bd9Sstevel@tonic-gate pd->fpclass = fp_quiet; 3317c478bd9Sstevel@tonic-gate form = 1; 3327c478bd9Sstevel@tonic-gate good = cp - 1; 3337c478bd9Sstevel@tonic-gate __nan_read = 1; 3347c478bd9Sstevel@tonic-gate if (current == L'(') { 3357c478bd9Sstevel@tonic-gate /* accept parenthesized string */ 3367c478bd9Sstevel@tonic-gate if (c99) { 3377c478bd9Sstevel@tonic-gate do { 3387c478bd9Sstevel@tonic-gate current = *++cp; 3397c478bd9Sstevel@tonic-gate } while (iswalnum(current) || 3407c478bd9Sstevel@tonic-gate current == L'_'); 3417c478bd9Sstevel@tonic-gate } else { 3427c478bd9Sstevel@tonic-gate do { 3437c478bd9Sstevel@tonic-gate current = *++cp; 3447c478bd9Sstevel@tonic-gate } while (current && 3457c478bd9Sstevel@tonic-gate current != L')'); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate if (current == L')') 3487c478bd9Sstevel@tonic-gate good = cp; 3497c478bd9Sstevel@tonic-gate } 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate goto done; 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate default: 3547c478bd9Sstevel@tonic-gate if (current == (wchar_t)decpt) { 3557c478bd9Sstevel@tonic-gate /* 3567c478bd9Sstevel@tonic-gate * Don't accept the radix point just yet; 3577c478bd9Sstevel@tonic-gate * we need to see at least one digit. 3587c478bd9Sstevel@tonic-gate */ 3597c478bd9Sstevel@tonic-gate current = *++cp; 3607c478bd9Sstevel@tonic-gate goto afterpoint; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate goto done; 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate nextnumber: 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * Admissible characters after the first digit are a valid 3697c478bd9Sstevel@tonic-gate * digit, an exponent delimiter (E or e for decimal form, 3707c478bd9Sstevel@tonic-gate * P or p for hex form), or the radix point. (Note that we 3717c478bd9Sstevel@tonic-gate * can't get here unless we've already found a digit.) 3727c478bd9Sstevel@tonic-gate */ 3737c478bd9Sstevel@tonic-gate if (NZDIGIT(current)) { 3747c478bd9Sstevel@tonic-gate /* 3757c478bd9Sstevel@tonic-gate * Found another nonzero digit. If there's enough room 3767c478bd9Sstevel@tonic-gate * in pd->ds, store any intervening zeros we've found so far 3777c478bd9Sstevel@tonic-gate * and then store this digit. Otherwise, stop storing 3787c478bd9Sstevel@tonic-gate * digits in pd->ds and set pd->more. 3797c478bd9Sstevel@tonic-gate */ 3807c478bd9Sstevel@tonic-gate if (ids + nzbp + 2 < DECIMAL_STRING_LENGTH) { 3817c478bd9Sstevel@tonic-gate for (i = 0; i < nzbp; i++) 3827c478bd9Sstevel@tonic-gate pd->ds[ids++] = '0'; 3837c478bd9Sstevel@tonic-gate pd->ds[ids++] = (char)current; 3847c478bd9Sstevel@tonic-gate } else { 3857c478bd9Sstevel@tonic-gate pd->exponent += (nzbp + 1) << expshift; 3867c478bd9Sstevel@tonic-gate pd->more = 1; 3877c478bd9Sstevel@tonic-gate if (ids < DECIMAL_STRING_LENGTH) { 3887c478bd9Sstevel@tonic-gate pd->ds[ids] = '\0'; 3897c478bd9Sstevel@tonic-gate pd->ndigits = ids; 3907c478bd9Sstevel@tonic-gate /* don't store any more digits */ 3917c478bd9Sstevel@tonic-gate ids = DECIMAL_STRING_LENGTH; 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate pd->fpclass = fp_normal; 3957c478bd9Sstevel@tonic-gate sigfound = 1; 3967c478bd9Sstevel@tonic-gate nzbp = 0; 3977c478bd9Sstevel@tonic-gate current = *++cp; 3987c478bd9Sstevel@tonic-gate 3997c478bd9Sstevel@tonic-gate /* 4007c478bd9Sstevel@tonic-gate * Use an optimized loop to grab a consecutive sequence 4017c478bd9Sstevel@tonic-gate * of nonzero digits quickly. 4027c478bd9Sstevel@tonic-gate */ 4037c478bd9Sstevel@tonic-gate nfastlimit = DECIMAL_STRING_LENGTH - 3 - ids; 4047c478bd9Sstevel@tonic-gate for (nfast = 0, pfast = &(pd->ds[ids]); 4057c478bd9Sstevel@tonic-gate nfast < nfastlimit && NZDIGIT(current); 4067c478bd9Sstevel@tonic-gate nfast++) { 4077c478bd9Sstevel@tonic-gate *pfast++ = (char)current; 4087c478bd9Sstevel@tonic-gate current = *++cp; 4097c478bd9Sstevel@tonic-gate } 4107c478bd9Sstevel@tonic-gate ids += nfast; 4117c478bd9Sstevel@tonic-gate if (current == L'0') 4127c478bd9Sstevel@tonic-gate goto nextnumberzero; /* common case */ 4137c478bd9Sstevel@tonic-gate /* advance good to the last accepted digit */ 4147c478bd9Sstevel@tonic-gate good = cp - 1; 4157c478bd9Sstevel@tonic-gate goto nextnumber; 4167c478bd9Sstevel@tonic-gate } else { 4177c478bd9Sstevel@tonic-gate switch (current) { 4187c478bd9Sstevel@tonic-gate case L'0': 4197c478bd9Sstevel@tonic-gate nextnumberzero: 4207c478bd9Sstevel@tonic-gate /* 4217c478bd9Sstevel@tonic-gate * Count zeros before the radix point. Later we 4227c478bd9Sstevel@tonic-gate * will either put these zeros into pd->ds or add 4237c478bd9Sstevel@tonic-gate * nzbp to pd->exponent to account for them. 4247c478bd9Sstevel@tonic-gate */ 4257c478bd9Sstevel@tonic-gate while (current == L'0') { 4267c478bd9Sstevel@tonic-gate nzbp++; 4277c478bd9Sstevel@tonic-gate current = *++cp; 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate good = cp - 1; 4307c478bd9Sstevel@tonic-gate goto nextnumber; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate case L'E': 4337c478bd9Sstevel@tonic-gate case L'e': 4347c478bd9Sstevel@tonic-gate if (form < 0) 4357c478bd9Sstevel@tonic-gate goto done; 4367c478bd9Sstevel@tonic-gate goto exponent; 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate case L'P': 4397c478bd9Sstevel@tonic-gate case L'p': 4407c478bd9Sstevel@tonic-gate if (form > 0) 4417c478bd9Sstevel@tonic-gate goto done; 4427c478bd9Sstevel@tonic-gate goto exponent; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate default: 4457c478bd9Sstevel@tonic-gate if (current == decpt) { 4467c478bd9Sstevel@tonic-gate /* accept the radix point */ 4477c478bd9Sstevel@tonic-gate good = cp; 4487c478bd9Sstevel@tonic-gate current = *++cp; 4497c478bd9Sstevel@tonic-gate goto afterpoint; 4507c478bd9Sstevel@tonic-gate } 4517c478bd9Sstevel@tonic-gate goto done; 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate } 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate afterpoint: 4567c478bd9Sstevel@tonic-gate /* 4577c478bd9Sstevel@tonic-gate * Admissible characters after the radix point are a valid digit 4587c478bd9Sstevel@tonic-gate * or an exponent delimiter. (Note that it is possible to get 4597c478bd9Sstevel@tonic-gate * here even though we haven't found any digits yet.) 4607c478bd9Sstevel@tonic-gate */ 4617c478bd9Sstevel@tonic-gate if (NZDIGIT(current)) { 4627c478bd9Sstevel@tonic-gate if (form == 0) 4637c478bd9Sstevel@tonic-gate form = 1; 4647c478bd9Sstevel@tonic-gate if (sigfound < 1) { 4657c478bd9Sstevel@tonic-gate /* no significant digits found until now */ 4667c478bd9Sstevel@tonic-gate pd->fpclass = fp_normal; 4677c478bd9Sstevel@tonic-gate sigfound = 1; 4687c478bd9Sstevel@tonic-gate pd->ds[ids++] = (char)current; 4697c478bd9Sstevel@tonic-gate pd->exponent = (-(nzap + 1)) << expshift; 4707c478bd9Sstevel@tonic-gate } else { 4717c478bd9Sstevel@tonic-gate /* significant digits have been found */ 4727c478bd9Sstevel@tonic-gate if (ids + nzbp + nzap + 2 < DECIMAL_STRING_LENGTH) { 4737c478bd9Sstevel@tonic-gate for (i = 0; i < nzbp + nzap; i++) 4747c478bd9Sstevel@tonic-gate pd->ds[ids++] = '0'; 4757c478bd9Sstevel@tonic-gate pd->ds[ids++] = (char)current; 4767c478bd9Sstevel@tonic-gate pd->exponent -= (nzap + 1) << expshift; 4777c478bd9Sstevel@tonic-gate } else { 4787c478bd9Sstevel@tonic-gate pd->exponent += nzbp << expshift; 4797c478bd9Sstevel@tonic-gate pd->more = 1; 4807c478bd9Sstevel@tonic-gate if (ids < DECIMAL_STRING_LENGTH) { 4817c478bd9Sstevel@tonic-gate pd->ds[ids] = '\0'; 4827c478bd9Sstevel@tonic-gate pd->ndigits = ids; 4837c478bd9Sstevel@tonic-gate /* don't store any more digits */ 4847c478bd9Sstevel@tonic-gate ids = DECIMAL_STRING_LENGTH; 4857c478bd9Sstevel@tonic-gate } 4867c478bd9Sstevel@tonic-gate } 4877c478bd9Sstevel@tonic-gate } 4887c478bd9Sstevel@tonic-gate nzbp = 0; 4897c478bd9Sstevel@tonic-gate nzap = 0; 4907c478bd9Sstevel@tonic-gate current = *++cp; 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate /* 4937c478bd9Sstevel@tonic-gate * Use an optimized loop to grab a consecutive sequence 4947c478bd9Sstevel@tonic-gate * of nonzero digits quickly. 4957c478bd9Sstevel@tonic-gate */ 4967c478bd9Sstevel@tonic-gate nfastlimit = DECIMAL_STRING_LENGTH - 3 - ids; 4977c478bd9Sstevel@tonic-gate for (nfast = 0, pfast = &(pd->ds[ids]); 4987c478bd9Sstevel@tonic-gate nfast < nfastlimit && NZDIGIT(current); 4997c478bd9Sstevel@tonic-gate nfast++) { 5007c478bd9Sstevel@tonic-gate *pfast++ = (char)current; 5017c478bd9Sstevel@tonic-gate current = *++cp; 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate ids += nfast; 5047c478bd9Sstevel@tonic-gate pd->exponent -= nfast << expshift; 5057c478bd9Sstevel@tonic-gate if (current == L'0') 5067c478bd9Sstevel@tonic-gate goto zeroafterpoint; 5077c478bd9Sstevel@tonic-gate /* advance good to the last accepted digit */ 5087c478bd9Sstevel@tonic-gate good = cp - 1; 5097c478bd9Sstevel@tonic-gate goto afterpoint; 5107c478bd9Sstevel@tonic-gate } else { 5117c478bd9Sstevel@tonic-gate switch (current) { 5127c478bd9Sstevel@tonic-gate case L'0': 5137c478bd9Sstevel@tonic-gate if (form == 0) 5147c478bd9Sstevel@tonic-gate form = 1; 5157c478bd9Sstevel@tonic-gate if (sigfound == -1) { 5167c478bd9Sstevel@tonic-gate pd->fpclass = fp_zero; 5177c478bd9Sstevel@tonic-gate sigfound = 0; 5187c478bd9Sstevel@tonic-gate } 5197c478bd9Sstevel@tonic-gate zeroafterpoint: 5207c478bd9Sstevel@tonic-gate /* 5217c478bd9Sstevel@tonic-gate * Count zeros after the radix point. If we find 5227c478bd9Sstevel@tonic-gate * any more nonzero digits later, we will put these 5237c478bd9Sstevel@tonic-gate * zeros into pd->ds and decrease pd->exponent by 5247c478bd9Sstevel@tonic-gate * nzap. 5257c478bd9Sstevel@tonic-gate */ 5267c478bd9Sstevel@tonic-gate while (current == L'0') { 5277c478bd9Sstevel@tonic-gate nzap++; 5287c478bd9Sstevel@tonic-gate current = *++cp; 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate good = cp - 1; 5317c478bd9Sstevel@tonic-gate goto afterpoint; 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate case L'E': 5347c478bd9Sstevel@tonic-gate case L'e': 5357c478bd9Sstevel@tonic-gate /* don't accept exponent without preceding digits */ 5367c478bd9Sstevel@tonic-gate if (sigfound == -1 || form < 0) 5377c478bd9Sstevel@tonic-gate goto done; 5387c478bd9Sstevel@tonic-gate break; 5397c478bd9Sstevel@tonic-gate 5407c478bd9Sstevel@tonic-gate case L'P': 5417c478bd9Sstevel@tonic-gate case L'p': 5427c478bd9Sstevel@tonic-gate /* don't accept exponent without preceding digits */ 5437c478bd9Sstevel@tonic-gate if (sigfound == -1 || form > 0) 5447c478bd9Sstevel@tonic-gate goto done; 5457c478bd9Sstevel@tonic-gate break; 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate default: 5487c478bd9Sstevel@tonic-gate goto done; 5497c478bd9Sstevel@tonic-gate } 5507c478bd9Sstevel@tonic-gate } 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate exponent: 5537c478bd9Sstevel@tonic-gate e = 0; 5547c478bd9Sstevel@tonic-gate esign = 0; 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate /* look for optional exponent sign */ 5577c478bd9Sstevel@tonic-gate current = *++cp; 5587c478bd9Sstevel@tonic-gate if (current == L'+') { 5597c478bd9Sstevel@tonic-gate current = *++cp; 5607c478bd9Sstevel@tonic-gate } else if (current == L'-') { 5617c478bd9Sstevel@tonic-gate esign = 1; 5627c478bd9Sstevel@tonic-gate current = *++cp; 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate 5657c478bd9Sstevel@tonic-gate /* 5667c478bd9Sstevel@tonic-gate * Accumulate explicit exponent. Note that if we don't find at 5677c478bd9Sstevel@tonic-gate * least one digit, good won't be updated and e will remain 0. 5687c478bd9Sstevel@tonic-gate * Also, we keep e from getting too large so we don't overflow 5697c478bd9Sstevel@tonic-gate * the range of int (but notice that the threshold is large 5707c478bd9Sstevel@tonic-gate * enough that any larger e would cause the result to underflow 5717c478bd9Sstevel@tonic-gate * or overflow anyway). 5727c478bd9Sstevel@tonic-gate */ 5737c478bd9Sstevel@tonic-gate while (L'0' <= current && current <= L'9') { 5747c478bd9Sstevel@tonic-gate good = cp; 5757c478bd9Sstevel@tonic-gate if (e <= 1000000) 5767c478bd9Sstevel@tonic-gate e = 10 * e + current - L'0'; 5777c478bd9Sstevel@tonic-gate current = *++cp; 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate if (esign) 5807c478bd9Sstevel@tonic-gate pd->exponent -= e; 5817c478bd9Sstevel@tonic-gate else 5827c478bd9Sstevel@tonic-gate pd->exponent += e; 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate done: 5857c478bd9Sstevel@tonic-gate /* 5867c478bd9Sstevel@tonic-gate * If we found any zeros before the radix point that were not 5877c478bd9Sstevel@tonic-gate * accounted for earlier, adjust the exponent. (This is only 5887c478bd9Sstevel@tonic-gate * relevant when pd->fpclass == fp_normal, but it's harmless 5897c478bd9Sstevel@tonic-gate * in all other cases.) 5907c478bd9Sstevel@tonic-gate */ 5917c478bd9Sstevel@tonic-gate pd->exponent += nzbp << expshift; 5927c478bd9Sstevel@tonic-gate 5937c478bd9Sstevel@tonic-gate /* terminate pd->ds if we haven't already */ 5947c478bd9Sstevel@tonic-gate if (ids < DECIMAL_STRING_LENGTH) { 5957c478bd9Sstevel@tonic-gate pd->ds[ids] = '\0'; 5967c478bd9Sstevel@tonic-gate pd->ndigits = ids; 5977c478bd9Sstevel@tonic-gate } 5987c478bd9Sstevel@tonic-gate 5997c478bd9Sstevel@tonic-gate /* 6007c478bd9Sstevel@tonic-gate * If we accepted any characters, advance *ppc to point to the 6017c478bd9Sstevel@tonic-gate * first character we didn't accept; otherwise, pass back a 6027c478bd9Sstevel@tonic-gate * signaling nan. 6037c478bd9Sstevel@tonic-gate */ 6047c478bd9Sstevel@tonic-gate if (good >= *ppc) { 6057c478bd9Sstevel@tonic-gate *ppc = good + 1; 6067c478bd9Sstevel@tonic-gate } else { 6077c478bd9Sstevel@tonic-gate pd->fpclass = fp_signaling; 6087c478bd9Sstevel@tonic-gate pd->sign = 0; 6097c478bd9Sstevel@tonic-gate form = 0; 6107c478bd9Sstevel@tonic-gate } 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate *pform = form; 6137c478bd9Sstevel@tonic-gate } 614