1/* $NetBSD: gethex.c,v 1.7 2020/02/22 00:38:14 kamil Exp $ */
2
3/****************************************************************
4
5The author of this software is David M. Gay.
6
7Copyright (C) 1998 by Lucent Technologies
8All Rights Reserved
9
10Permission to use, copy, modify, and distribute this software and
11its documentation for any purpose and without fee is hereby
12granted, provided that the above copyright notice appear in all
13copies and that both that the copyright notice and this
14permission notice and warranty disclaimer appear in supporting
15documentation, and that the name of Lucent or any of its entities
16not be used in advertising or publicity pertaining to
17distribution of the software without specific, written prior
18permission.
19
20LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
21INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
22IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
23SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
24WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
25IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
26ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
27THIS SOFTWARE.
28
29****************************************************************/
30
31/* Please send bug reports to David M. Gay (dmg at acm dot org,
32 * with " at " changed at "@" and " dot " changed to ".").	*/
33
34#include "gdtoaimp.h"
35
36#ifdef USE_LOCALE
37#include "locale.h"
38#endif
39
40 int
41gethex( CONST char **sp, CONST FPI *fpi, Long *expt, Bigint **bp, int sign, locale_t loc)
42{
43	Bigint *b;
44	CONST char *decpt, *s, *s0, *s1;
45	int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
46	ULong L, lostbits, *x;
47	Long e, e1;
48#ifdef USE_LOCALE
49	int i;
50	const char *decimalpoint = localeconv_l(loc)->decimal_point;
51#endif
52
53	if (!hexdig[(unsigned char)'0'])
54		hexdig_init_D2A();
55	*bp = 0;
56	havedig = 0;
57	s0 = *(CONST char **)sp + 2;
58	while(s0[havedig] == '0')
59		havedig++;
60	s0 += havedig;
61	s = s0;
62	decpt = 0;
63	zret = 0;
64	e = 0;
65	if (hexdig[(unsigned char)*s])
66		havedig++;
67	else {
68		zret = 1;
69#ifdef USE_LOCALE
70		for(i = 0; decimalpoint[i]; ++i) {
71			if (s[i] != decimalpoint[i])
72				goto pcheck;
73			}
74		decpt = s += i;
75#else
76		if (*s != '.')
77			goto pcheck;
78		decpt = ++s;
79#endif
80		if (!hexdig[(unsigned char)*s])
81			goto pcheck;
82		while(*s == '0')
83			s++;
84		if (hexdig[(unsigned char)*s])
85			zret = 0;
86		havedig = 1;
87		s0 = s;
88		}
89	while(hexdig[(unsigned char)*s])
90		s++;
91#ifdef USE_LOCALE
92	if (*s == *decimalpoint && !decpt) {
93		for(i = 1; decimalpoint[i]; ++i) {
94			if (s[i] != decimalpoint[i])
95				goto pcheck;
96			}
97		decpt = s += i;
98#else
99	if (*s == '.' && !decpt) {
100		decpt = ++s;
101#endif
102		while(hexdig[(unsigned char)*s])
103			s++;
104		}/*}*/
105	if (decpt)
106		e = -(((Long)(s-decpt)) << 2);
107 pcheck:
108	s1 = s;
109	big = esign = 0;
110	switch(*s) {
111	  case 'p':
112	  case 'P':
113		switch(*++s) {
114		  case '-':
115			esign = 1;
116			/* FALLTHROUGH */
117		  case '+':
118			s++;
119		  }
120		if ((n = hexdig[(unsigned char)*s]) == 0 || n > 0x19) {
121			s = s1;
122			break;
123			}
124		e1 = n - 0x10;
125		while((n = hexdig[(unsigned char)*++s]) !=0 && n <= 0x19) {
126			if (e1 & 0xf8000000)
127				big = 1;
128			e1 = 10*e1 + n - 0x10;
129			}
130		if (esign)
131			e1 = -e1;
132		e += e1;
133	  }
134	*sp = __UNCONST(s);
135	if (!havedig)
136		*sp = (char*)__UNCONST(s0) - 1;
137	if (zret)
138		return STRTOG_Zero;
139	if (big) {
140		if (esign) {
141			switch(fpi->rounding) {
142			  case FPI_Round_up:
143				if (sign)
144					break;
145				goto ret_tiny;
146			  case FPI_Round_down:
147				if (!sign)
148					break;
149				goto ret_tiny;
150			  }
151			goto retz;
152 ret_tiny:
153			b = Balloc(0);
154			b->wds = 1;
155			b->x[0] = 1;
156			goto dret;
157			}
158		switch(fpi->rounding) {
159		  case FPI_Round_near:
160			goto ovfl1;
161		  case FPI_Round_up:
162			if (!sign)
163				goto ovfl1;
164			goto ret_big;
165		  case FPI_Round_down:
166			if (sign)
167				goto ovfl1;
168			goto ret_big;
169		  }
170 ret_big:
171		nbits = fpi->nbits;
172		n0 = n = (unsigned int)nbits >> kshift;
173		if (nbits & kmask)
174			++n;
175		for(j = n, k = 0; (j = (unsigned int)j >> 1) != 0; ++k);
176		*bp = b = Balloc(k);
177		b->wds = n;
178		for(j = 0; j < n0; ++j)
179			b->x[j] = ALL_ON;
180		if (n > n0)
181			b->x[j] = ULbits >> (ULbits - (nbits & kmask));
182		*expt = fpi->emin;
183		return STRTOG_Normal | STRTOG_Inexlo;
184		}
185	n = (int)(s1 - s0) - 1;
186	for(k = 0; n > (1 << (kshift-2)) - 1; n = (unsigned int)n >> 1)
187		k++;
188	b = Balloc(k);
189	if (b == NULL)
190		return STRTOG_NoMemory;
191	x = b->x;
192	n = 0;
193	L = 0;
194#ifdef USE_LOCALE
195	for(i = 0; decimalpoint[i+1]; ++i);
196#endif
197	while(s1 > s0) {
198#ifdef USE_LOCALE
199		if (*--s1 == decimalpoint[i]) {
200			s1 -= i;
201			continue;
202			}
203#else
204		if (*--s1 == '.')
205			continue;
206#endif
207		if (n == ULbits) {
208			*x++ = L;
209			L = 0;
210			n = 0;
211			}
212		L |= (unsigned int)(hexdig[(unsigned char)*s1] & 0x0f) << n;
213		n += 4;
214		}
215	*x++ = L;
216	b->wds = n = (int)(x - b->x);
217	n = ULbits*n - hi0bits(L);
218	nbits = fpi->nbits;
219	lostbits = 0;
220	x = b->x;
221	if (n > nbits) {
222		n -= nbits;
223		if (any_on(b,n)) {
224			lostbits = 1;
225			k = n - 1;
226			if (x[(unsigned int)k>>kshift] & 1 << (k & kmask)) {
227				lostbits = 2;
228				if (k > 0 && any_on(b,k))
229					lostbits = 3;
230				}
231			}
232		rshift(b, n);
233		e += n;
234		}
235	else if (n < nbits) {
236		n = nbits - n;
237		b = lshift(b, n);
238		if (b == NULL)
239			return STRTOG_NoMemory;
240		e -= n;
241		x = b->x;
242		}
243	if (e > fpi->emax) {
244 ovfl:
245		Bfree(b);
246 ovfl1:
247#ifndef NO_ERRNO
248		errno = ERANGE;
249#endif
250		return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
251		}
252	irv = STRTOG_Normal;
253	if (e < fpi->emin) {
254		irv = STRTOG_Denormal;
255		n = fpi->emin - e;
256		if (n >= nbits) {
257			switch (fpi->rounding) {
258			  case FPI_Round_near:
259				if (n == nbits && (n < 2 || any_on(b,n-1)))
260					goto one_bit;
261				break;
262			  case FPI_Round_up:
263				if (!sign)
264					goto one_bit;
265				break;
266			  case FPI_Round_down:
267				if (sign) {
268 one_bit:
269					x[0] = b->wds = 1;
270 dret:
271					*bp = b;
272					*expt = fpi->emin;
273#ifndef NO_ERRNO
274					errno = ERANGE;
275#endif
276					return STRTOG_Denormal | STRTOG_Inexhi
277						| STRTOG_Underflow;
278					}
279			  }
280			Bfree(b);
281 retz:
282#ifndef NO_ERRNO
283			errno = ERANGE;
284#endif
285			return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
286			}
287		k = n - 1;
288		if (lostbits)
289			lostbits = 1;
290		else if (k > 0)
291			lostbits = any_on(b,k);
292		if (x[(unsigned int)k>>kshift] & 1 << (k & kmask))
293			lostbits |= 2;
294		nbits -= n;
295		rshift(b,n);
296		e = fpi->emin;
297		}
298	if (lostbits) {
299		up = 0;
300		switch(fpi->rounding) {
301		  case FPI_Round_zero:
302			break;
303		  case FPI_Round_near:
304			if (lostbits & 2
305			 && (lostbits | x[0]) & 1)
306				up = 1;
307			break;
308		  case FPI_Round_up:
309			up = 1 - sign;
310			break;
311		  case FPI_Round_down:
312			up = sign;
313		  }
314		if (up) {
315			k = b->wds;
316			b = increment(b);
317			x = b->x;
318			if (irv == STRTOG_Denormal) {
319				if (nbits == fpi->nbits - 1
320				 && x[(unsigned int)nbits >> kshift] & 1 << (nbits & kmask))
321					irv =  STRTOG_Normal;
322				}
323			else if (b->wds > k
324			 || ((n = nbits & kmask) !=0
325			      && hi0bits(x[k-1]) < 32-n)) {
326				rshift(b,1);
327				if (++e > fpi->emax)
328					goto ovfl;
329				}
330			irv |= STRTOG_Inexhi;
331			}
332		else
333			irv |= STRTOG_Inexlo;
334		}
335	*bp = b;
336	*expt = e;
337	return irv;
338	}
339