1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1988 AT&T	*/
28/*	  All Rights Reserved  	*/
29
30#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32/*
33 * This file is based on /usr/src/lib/libc/port/gen/strtod.c and
34 * /usr/src/lib/libc/sparc/fp/string_decim.c
35 */
36
37#pragma weak _wcstod = wcstod
38#pragma weak _wstod = wstod
39
40#include "lint.h"
41#include <errno.h>
42#include <stdio.h>
43#include <values.h>
44#include <floatingpoint.h>
45#include <stddef.h>
46#include <wctype.h>
47#include "base_conversion.h"	/* from usr/src/lib/libc/inc */
48#include <locale.h>
49#include "libc.h"
50#include "xpg6.h"
51
52static void wstring_to_decimal(const wchar_t **, int, decimal_record *, int *);
53
54double
55wcstod(const wchar_t *cp, wchar_t **ptr)
56{
57	double		x;
58	decimal_mode	mr;
59	decimal_record	dr;
60	fp_exception_field_type fs;
61	int 		form;
62
63	wstring_to_decimal(&cp, __xpg6 & _C99SUSv3_recognize_hexfp, &dr, &form);
64	if (ptr != NULL)
65		*ptr = (wchar_t *)cp;
66	if (form == 0)
67		return (0.0);	/* Shameful kluge for SVID's sake. */
68#if defined(__i386) || defined(__amd64)
69	mr.rd = __xgetRD();
70#elif defined(__sparc)
71	mr.rd = _QgetRD();
72#else
73#error Unknown architecture!
74#endif
75	if (form < 0)
76		__hex_to_double(&dr, mr.rd, &x, &fs);
77	else
78		decimal_to_double(&x, &mr, &dr, &fs);
79	if (fs & ((1 << fp_overflow) | (1 << fp_underflow)))
80		errno = ERANGE;
81	return (x);
82}
83
84float
85wcstof(const wchar_t *cp, wchar_t **ptr)
86{
87	float		x;
88	decimal_mode	mr;
89	decimal_record	dr;
90	fp_exception_field_type fs;
91	int		form;
92
93	wstring_to_decimal(&cp, 1, &dr, &form);
94	if (ptr != NULL)
95		*ptr = (wchar_t *)cp;
96	if (form == 0)
97		return (0.0f);
98#if defined(__i386) || defined(__amd64)
99	mr.rd = __xgetRD();
100#elif defined(__sparc)
101	mr.rd = _QgetRD();
102#else
103#error Unknown architecture!
104#endif
105	if (form < 0)
106		__hex_to_single(&dr, mr.rd, &x, &fs);
107	else
108		decimal_to_single(&x, &mr, &dr, &fs);
109	if (fs & ((1 << fp_overflow) | (1 << fp_underflow)))
110		errno = ERANGE;
111	return (x);
112}
113
114long double
115wcstold(const wchar_t *cp, wchar_t **ptr)
116{
117	long double	x;
118	decimal_mode	mr;
119	decimal_record	dr;
120	fp_exception_field_type fs;
121	int		form;
122
123	wstring_to_decimal(&cp, 1, &dr, &form);
124	if (ptr != NULL)
125		*ptr = (wchar_t *)cp;
126	if (form == 0)
127		return (0.0L);
128#if defined(__i386) || defined(__amd64)
129	mr.rd = __xgetRD();
130	if (form < 0)
131		__hex_to_extended(&dr, mr.rd, (extended *)&x, &fs);
132	else
133		decimal_to_extended((extended *)&x, &mr, &dr, &fs);
134#elif defined(__sparc)
135	mr.rd = _QgetRD();
136	if (form < 0)
137		__hex_to_quadruple(&dr, mr.rd, &x, &fs);
138	else
139		decimal_to_quadruple(&x, &mr, &dr, &fs);
140#else
141#error Unknown architecture!
142#endif
143	if (fs & ((1 << fp_overflow) | (1 << fp_underflow)))
144		errno = ERANGE;
145	return (x);
146}
147
148double
149wstod(const wchar_t *cp, wchar_t **ptr)
150{
151	return (wcstod(cp, ptr));
152}
153
154static const char *infstring = "INFINITY";
155static const char *nanstring = "NAN";
156
157/*
158 * The following macro is applied to wchar_t arguments solely for the
159 * purpose of comparing the result with one of the characters in the
160 * strings above.
161 */
162#define	UCASE(c)	(((L'a' <= c) && (c <= L'z'))? c - 32 : c)
163
164/*
165 * The following macro yields an expression that is true whenever
166 * the argument is a valid nonzero digit for the form being parsed.
167 */
168#define	NZDIGIT(c)	((L'1' <= c && c <= L'9') || (form < 0 && \
169			((L'a' <= c && c <= L'f') || (L'A' <= c && c <= L'F'))))
170
171/*
172 * wstring_to_decimal is modelled on string_to_decimal, the majority
173 * of which can be found in the common file char_to_decimal.h.  The
174 * significant differences are:
175 *
176 * 1. This code recognizes only C99 (hex fp strings and restricted
177 *    characters in parentheses following "nan") vs. C90 modes, no
178 *    Fortran conventions.
179 *
180 * 2. *pform is an int rather than an enum decimal_string_form.  On
181 *    return, *pform == 0 if no valid token was found, *pform < 0
182 *    if a C99 hex fp string was found, and *pform > 0 if a decimal
183 *    string was found.
184 */
185static void
186wstring_to_decimal(const wchar_t **ppc, int c99, decimal_record *pd,
187    int *pform)
188{
189	const wchar_t	*cp = *ppc; /* last character seen */
190	const wchar_t	*good = cp - 1;	/* last character accepted */
191	wchar_t		current; /* always equal to *cp */
192	int		sigfound;
193	int		ids = 0;
194	int		i, agree;
195	int		nzbp = 0; /* number of zeros before point */
196	int		nzap = 0; /* number of zeros after point */
197	char		decpt;
198	int		nfast, nfastlimit;
199	char		*pfast;
200	int		e, esign;
201	int		expshift = 0;
202	int		form;
203
204	/*
205	 * This routine assumes that the radix point is a single
206	 * ASCII character, so that following this assignment, the
207	 * condition (current == decpt) will correctly detect it.
208	 */
209	decpt = *(localeconv()->decimal_point);
210
211	/* input is invalid until we find something */
212	pd->fpclass = fp_signaling;
213	pd->sign = 0;
214	pd->exponent = 0;
215	pd->ds[0] = '\0';
216	pd->more = 0;
217	pd->ndigits = 0;
218	*pform = form = 0;
219
220	/* skip white space */
221	current = *cp;
222	while (iswspace((wint_t)current))
223		current = *++cp;
224
225	/* look for optional leading sign */
226	if (current == L'+') {
227		current = *++cp;
228	} else if (current == L'-') {
229		pd->sign = 1;
230		current = *++cp;
231	}
232
233	sigfound = -1;		/* -1 = no digits found yet */
234
235	/*
236	 * Admissible first non-white-space, non-sign characters are
237	 * 0-9, i, I, n, N, or the radix point.
238	 */
239	if (L'1' <= current && current <= L'9') {
240		pd->fpclass = fp_normal;
241		form = 1;
242		good = cp;
243		sigfound = 1;	/* 1 = significant digits found */
244		pd->ds[ids++] = (char)current;
245		current = *++cp;
246	} else {
247		switch (current) {
248		case L'0':
249			/*
250			 * Accept the leading zero and set pd->fpclass
251			 * accordingly, but don't set sigfound until we
252			 * determine that this isn't a "fake" hex string
253			 * (i.e., 0x.p...).
254			 */
255			good = cp;
256			pd->fpclass = fp_zero;
257			if (c99) {
258				/* look for a hex fp string */
259				current = *++cp;
260				if (current == L'X' || current == L'x') {
261					/* assume hex fp form */
262					form = -1;
263					expshift = 2;
264					current = *++cp;
265					/*
266					 * Only a digit or radix point can
267					 * follow "0x".
268					 */
269					if (NZDIGIT(current)) {
270						pd->fpclass = fp_normal;
271						good = cp;
272						sigfound = 1;
273						pd->ds[ids++] = (char)current;
274						current = *++cp;
275						break;
276					} else if (current == (wchar_t)decpt) {
277						current = *++cp;
278						goto afterpoint;
279					} else if (current != L'0') {
280						/* not hex fp after all */
281						form = 1;
282						expshift = 0;
283						goto done;
284					}
285				} else {
286					form = 1;
287				}
288			} else {
289				form = 1;
290			}
291
292			/* skip all leading zeros */
293			while (current == L'0')
294				current = *++cp;
295			good = cp - 1;
296			sigfound = 0;	/* 0 = only zeros found so far */
297			break;
298
299		case L'i':
300		case L'I':
301			/* look for inf or infinity */
302			current = *++cp;
303			agree = 1;
304			while (agree <= 7 &&
305			    UCASE(current) == (wchar_t)infstring[agree]) {
306				current = *++cp;
307				agree++;
308			}
309			if (agree >= 3) {
310				/* found valid infinity */
311				pd->fpclass = fp_infinity;
312				form = 1;
313				good = (agree < 8)? cp + 2 - agree : cp - 1;
314				__inf_read = 1;
315			}
316			goto done;
317
318		case L'n':
319		case L'N':
320			/* look for nan or nan(string) */
321			current = *++cp;
322			agree = 1;
323			while (agree <= 2 &&
324			    UCASE(current) == (wchar_t)nanstring[agree]) {
325				current = *++cp;
326				agree++;
327			}
328			if (agree == 3) {
329				/* found valid NaN */
330				pd->fpclass = fp_quiet;
331				form = 1;
332				good = cp - 1;
333				__nan_read = 1;
334				if (current == L'(') {
335					/* accept parenthesized string */
336					if (c99) {
337						do {
338							current = *++cp;
339						} while (iswalnum(current) ||
340						    current == L'_');
341					} else {
342						do {
343							current = *++cp;
344						} while (current &&
345						    current != L')');
346					}
347					if (current == L')')
348						good = cp;
349				}
350			}
351			goto done;
352
353		default:
354			if (current == (wchar_t)decpt) {
355				/*
356				 * Don't accept the radix point just yet;
357				 * we need to see at least one digit.
358				 */
359				current = *++cp;
360				goto afterpoint;
361			}
362			goto done;
363		}
364	}
365
366nextnumber:
367	/*
368	 * Admissible characters after the first digit are a valid
369	 * digit, an exponent delimiter (E or e for decimal form,
370	 * P or p for hex form), or the radix point.  (Note that we
371	 * can't get here unless we've already found a digit.)
372	 */
373	if (NZDIGIT(current)) {
374		/*
375		 * Found another nonzero digit.  If there's enough room
376		 * in pd->ds, store any intervening zeros we've found so far
377		 * and then store this digit.  Otherwise, stop storing
378		 * digits in pd->ds and set pd->more.
379		 */
380		if (ids + nzbp + 2 < DECIMAL_STRING_LENGTH) {
381			for (i = 0; i < nzbp; i++)
382				pd->ds[ids++] = '0';
383			pd->ds[ids++] = (char)current;
384		} else {
385			pd->exponent += (nzbp + 1) << expshift;
386			pd->more = 1;
387			if (ids < DECIMAL_STRING_LENGTH) {
388				pd->ds[ids] = '\0';
389				pd->ndigits = ids;
390				/* don't store any more digits */
391				ids = DECIMAL_STRING_LENGTH;
392			}
393		}
394		pd->fpclass = fp_normal;
395		sigfound = 1;
396		nzbp = 0;
397		current = *++cp;
398
399		/*
400		 * Use an optimized loop to grab a consecutive sequence
401		 * of nonzero digits quickly.
402		 */
403		nfastlimit = DECIMAL_STRING_LENGTH - 3 - ids;
404		for (nfast = 0, pfast = &(pd->ds[ids]);
405		    nfast < nfastlimit && NZDIGIT(current);
406		    nfast++) {
407			*pfast++ = (char)current;
408			current = *++cp;
409		}
410		ids += nfast;
411		if (current == L'0')
412			goto nextnumberzero;	/* common case */
413		/* advance good to the last accepted digit */
414		good = cp - 1;
415		goto nextnumber;
416	} else {
417		switch (current) {
418		case L'0':
419nextnumberzero:
420			/*
421			 * Count zeros before the radix point.  Later we
422			 * will either put these zeros into pd->ds or add
423			 * nzbp to pd->exponent to account for them.
424			 */
425			while (current == L'0') {
426				nzbp++;
427				current = *++cp;
428			}
429			good = cp - 1;
430			goto nextnumber;
431
432		case L'E':
433		case L'e':
434			if (form < 0)
435				goto done;
436			goto exponent;
437
438		case L'P':
439		case L'p':
440			if (form > 0)
441				goto done;
442			goto exponent;
443
444		default:
445			if (current == decpt) {
446				/* accept the radix point */
447				good = cp;
448				current = *++cp;
449				goto afterpoint;
450			}
451			goto done;
452		}
453	}
454
455afterpoint:
456	/*
457	 * Admissible characters after the radix point are a valid digit
458	 * or an exponent delimiter.  (Note that it is possible to get
459	 * here even though we haven't found any digits yet.)
460	 */
461	if (NZDIGIT(current)) {
462		if (form == 0)
463			form = 1;
464		if (sigfound < 1) {
465			/* no significant digits found until now */
466			pd->fpclass = fp_normal;
467			sigfound = 1;
468			pd->ds[ids++] = (char)current;
469			pd->exponent = (-(nzap + 1)) << expshift;
470		} else {
471			/* significant digits have been found */
472			if (ids + nzbp + nzap + 2 < DECIMAL_STRING_LENGTH) {
473				for (i = 0; i < nzbp + nzap; i++)
474					pd->ds[ids++] = '0';
475				pd->ds[ids++] = (char)current;
476				pd->exponent -= (nzap + 1) << expshift;
477			} else {
478				pd->exponent += nzbp << expshift;
479				pd->more = 1;
480				if (ids < DECIMAL_STRING_LENGTH) {
481					pd->ds[ids] = '\0';
482					pd->ndigits = ids;
483					/* don't store any more digits */
484					ids = DECIMAL_STRING_LENGTH;
485				}
486			}
487		}
488		nzbp = 0;
489		nzap = 0;
490		current = *++cp;
491
492		/*
493		 * Use an optimized loop to grab a consecutive sequence
494		 * of nonzero digits quickly.
495		 */
496		nfastlimit = DECIMAL_STRING_LENGTH - 3 - ids;
497		for (nfast = 0, pfast = &(pd->ds[ids]);
498		    nfast < nfastlimit && NZDIGIT(current);
499		    nfast++) {
500			*pfast++ = (char)current;
501			current = *++cp;
502		}
503		ids += nfast;
504		pd->exponent -= nfast << expshift;
505		if (current == L'0')
506			goto zeroafterpoint;
507		/* advance good to the last accepted digit */
508		good = cp - 1;
509		goto afterpoint;
510	} else {
511		switch (current) {
512		case L'0':
513			if (form == 0)
514				form = 1;
515			if (sigfound == -1) {
516				pd->fpclass = fp_zero;
517				sigfound = 0;
518			}
519zeroafterpoint:
520			/*
521			 * Count zeros after the radix point.  If we find
522			 * any more nonzero digits later, we will put these
523			 * zeros into pd->ds and decrease pd->exponent by
524			 * nzap.
525			 */
526			while (current == L'0') {
527				nzap++;
528				current = *++cp;
529			}
530			good = cp - 1;
531			goto afterpoint;
532
533		case L'E':
534		case L'e':
535			/* don't accept exponent without preceding digits */
536			if (sigfound == -1 || form < 0)
537				goto done;
538			break;
539
540		case L'P':
541		case L'p':
542			/* don't accept exponent without preceding digits */
543			if (sigfound == -1 || form > 0)
544				goto done;
545			break;
546
547		default:
548			goto done;
549		}
550	}
551
552exponent:
553	e = 0;
554	esign = 0;
555
556	/* look for optional exponent sign */
557	current = *++cp;
558	if (current == L'+') {
559		current = *++cp;
560	} else if (current == L'-') {
561		esign = 1;
562		current = *++cp;
563	}
564
565	/*
566	 * Accumulate explicit exponent.  Note that if we don't find at
567	 * least one digit, good won't be updated and e will remain 0.
568	 * Also, we keep e from getting too large so we don't overflow
569	 * the range of int (but notice that the threshold is large
570	 * enough that any larger e would cause the result to underflow
571	 * or overflow anyway).
572	 */
573	while (L'0' <= current && current <= L'9') {
574		good = cp;
575		if (e <= 1000000)
576			e = 10 * e + current - L'0';
577		current = *++cp;
578	}
579	if (esign)
580		pd->exponent -= e;
581	else
582		pd->exponent += e;
583
584done:
585	/*
586	 * If we found any zeros before the radix point that were not
587	 * accounted for earlier, adjust the exponent.  (This is only
588	 * relevant when pd->fpclass == fp_normal, but it's harmless
589	 * in all other cases.)
590	 */
591	pd->exponent += nzbp << expshift;
592
593	/* terminate pd->ds if we haven't already */
594	if (ids < DECIMAL_STRING_LENGTH) {
595		pd->ds[ids] = '\0';
596		pd->ndigits = ids;
597	}
598
599	/*
600	 * If we accepted any characters, advance *ppc to point to the
601	 * first character we didn't accept; otherwise, pass back a
602	 * signaling nan.
603	 */
604	if (good >= *ppc) {
605		*ppc = good + 1;
606	} else {
607		pd->fpclass = fp_signaling;
608		pd->sign = 0;
609		form = 0;
610	}
611
612	*pform = form;
613}
614