1 /* @(#)cvt.c	1.11 16/02/08 Copyright 1998-2016 J. Schilling */
2 /*
3  *	Compatibility routines for 4.4BSD based C-libraries ecvt()/fcvt()
4  *	and a working gcvt() that is needed on 4.4BSD and GNU libc systems.
5  *
6  *	On 4.4BSD, gcvt() is missing, the gcvt() implementation from GNU libc
7  *	is not working correctly.
8  *
9  *	Neither __dtoa() nor [efg]cvt() are MT safe.
10  *
11  *	Copyright (c) 1998-2016 J. Schilling
12  */
13 /*
14  * The contents of this file are subject to the terms of the
15  * Common Development and Distribution License, Version 1.0 only
16  * (the "License").  You may not use this file except in compliance
17  * with the License.
18  *
19  * See the file CDDL.Schily.txt in this distribution for details.
20  * A copy of the CDDL is also available via the Internet at
21  * http://www.opensource.org/licenses/cddl1.txt
22  *
23  * When distributing Covered Code, include this CDDL HEADER in each
24  * file and include the License file CDDL.Schily.txt from this distribution.
25  */
26 
27 #include <schily/stdlib.h>
28 #include <schily/utypes.h>
29 #include <schily/standard.h>
30 #include <schily/nlsdefs.h>
31 
32 #ifdef	HAVE_DTOA	/* 4.4BSD floating point implementation */
33 #ifdef	HAVE_DTOA_R
34 extern	char *__dtoa	__PR((double value, int mode, int ndigit, int *decpt,
35 					int *sign, char **ep, char **resultp));
36 #else
37 extern	char *__dtoa	__PR((double value, int mode, int ndigit, int *decpt,
38 					int *sign, char **ep));
39 #endif
40 #else
41 
42 #if	!defined(HAVE_ECVT)
43 /*
44  * As a hint from Thomas Langer <Langer.Thomas@gmx.net>, we use strtod.c in
45  * hope that we then will be able to print floating point numbers on all
46  * platforms, even those without *cvt() and without __dtoa() in libc.
47  *
48  * ... of course, we need to decide when we need to include strtod.c ...
49  *
50  * We come into this file if autoconf found that gcvt(), fcvt() or ecvt() is
51  * missing. If we are on a *BSD alike system, there is __dtoa() but neither
52  * gcvt() nor fcvt() or ecvt(), so we emulate all three functions via __dtoa().
53  * Glibc has a buggy gcvt() which causes an endless recursion,
54  * fcvt() from Cygwin32 is buggy, so we emulate fcvt() via ecvt() on Cygwin.
55  *
56  * If at least ecvt() is present, we don't need __dtoa() from strtod.c
57  */
58 #include "strtod.c"
59 #define	HAVE_DTOA
60 #endif	/* !defined(HAVE_ECVT) */
61 
62 #endif	/* HAVE_DTOA */
63 
64 #ifndef	HAVE_ECVT
65 EXPORT	char *ecvt	__PR((double value, int ndigit, int *decpt, int *sign));
66 #endif
67 #ifndef	HAVE_FCVT
68 EXPORT	char *fcvt	__PR((double value, int ndigit, int *decpt, int *sign));
69 #endif
70 #ifndef	HAVE_GCVT
71 EXPORT	char *gcvt	__PR((double value, int ndigit, char *buf));
72 #endif
73 
74 #if	!defined(HAVE_ECVT) && defined(HAVE_DTOA)
75 #define	HAVE_ECVT
76 char *
ecvt(value,ndigit,decpt,sign)77 ecvt(value, ndigit, decpt, sign)
78 	double	value;
79 	int	ndigit;
80 	int	*decpt;
81 	int	*sign;
82 {
83 static	Uint	bufsize;
84 static	char	*buf;
85 	char	*bufend;
86 	char	*ep;
87 	char	*bp;
88 #ifdef	HAVE_DTOA_R
89 static	char	*result;
90 #endif
91 
92 #ifdef	HAVE_DTOA_R
93 	if (result) {
94 		free(result);
95 		result = NULL;
96 	}
97 	bp = __dtoa(value, 2, ndigit, decpt, sign, &ep, &result);
98 #else
99 	bp = __dtoa(value, 2, ndigit, decpt, sign, &ep);
100 #endif
101 
102 	if (value == 0.0) {
103 		/*
104 		 * Handle __dtoa()'s deviation from ecvt():
105 		 * 0.0 is converted to "0" instead of 'ndigit' zeroes.
106 		 * The string "0" is not allocated, so
107 		 * we need to allocate buffer to hold 'ndigit' zeroes.
108 		 */
109 		if (bufsize < ndigit + 1) {
110 			if (buf != (char *)0)
111 				free(buf);
112 			bufsize = ndigit + 1;
113 			buf = malloc(bufsize);
114 		}
115 		ep = bp = buf;
116 	}
117 
118 	/*
119 	 * Fill up trailing zeroes suppressed by __dtoa()
120 	 * From an internal __dtoa() comment:
121 	 *	Sufficient space is allocated to the return value
122 	 *	to hold the suppressed trailing zeros.
123 	 */
124 	for (bufend = &bp[ndigit]; ep < bufend; )
125 		*ep++ = '0';
126 	*ep = '\0';
127 
128 	return (bp);
129 }
130 #endif
131 
132 #if	!defined(HAVE_FCVT) && defined(HAVE_DTOA)
133 #define	HAVE_FCVT
134 char *
fcvt(value,ndigit,decpt,sign)135 fcvt(value, ndigit, decpt, sign)
136 	double	value;
137 	int	ndigit;
138 	int	*decpt;
139 	int	*sign;
140 {
141 static	Uint	bufsize;
142 static	char	*buf;
143 	char	*bufend;
144 	char	*ep;
145 	char	*bp;
146 #ifdef	HAVE_DTOA_R
147 static	char	*result;
148 #endif
149 
150 #ifdef	HAVE_DTOA_R
151 	if (result) {
152 		free(result);
153 		result = NULL;
154 	}
155 	bp = __dtoa(value, 3, ndigit, decpt, sign, &ep, &result);
156 #else
157 	bp = __dtoa(value, 3, ndigit, decpt, sign, &ep);
158 #endif
159 
160 	if (value == 0.0) {
161 		/*
162 		 * Handle __dtoa()'s deviation from fcvt():
163 		 * 0.0 is converted to "0" instead of 'ndigit' zeroes.
164 		 * The string "0" is not allocated, so
165 		 * we need to allocate buffer to hold 'ndigit' zeroes.
166 		 */
167 		if (bufsize < ndigit + 1) {
168 			if (buf != (char *)0)
169 				free(buf);
170 			bufsize = ndigit + 1;
171 			buf = malloc(bufsize);
172 		}
173 		ep = bp = buf;
174 		*decpt = 0;
175 	}
176 
177 	/*
178 	 * Fill up trailing zeroes suppressed by __dtoa()
179 	 * From an internal __dtoa() comment:
180 	 *	Sufficient space is allocated to the return value
181 	 *	to hold the suppressed trailing zeros.
182 	 */
183 	for (bufend = &bp[*decpt + ndigit]; ep < bufend; )
184 		*ep++ = '0';
185 	*ep = '\0';
186 
187 	return (bp);
188 }
189 #endif
190 
191 #ifndef	HAVE_GCVT
192 #define	HAVE_GCVT
193 char *
gcvt(number,ndigit,buf)194 gcvt(number, ndigit, buf)
195 	double	number;
196 	int	ndigit;
197 	char	*buf;
198 {
199 		int	sign;
200 		int	decpt;
201 		char	dpoint;
202 	register char	*b;
203 	register char	*rs;
204 	register int	i;
205 
206 #if defined(HAVE_LOCALECONV) && defined(USE_LOCALE)
207 	dpoint = *(localeconv()->decimal_point);
208 #else
209 	dpoint = '.';
210 #endif
211 	b = ecvt(number, ndigit, &decpt, &sign);
212 	rs = buf;
213 	if (sign)
214 		*rs++ = '-';
215 	for (i = ndigit-1; i > 0 && b[i] == '0'; i--)
216 		ndigit--;
217 #ifdef	V7_FLOATSTYLE
218 	if ((decpt >= 0 && decpt-ndigit > 4) ||
219 #else
220 	if ((decpt >= 0 && decpt-ndigit > 0) ||
221 #endif
222 	    (decpt < -3)) {			/* e-format */
223 		decpt--;
224 		*rs++ = *b++;
225 		*rs++ = dpoint;			/* '.' */
226 		for (i = 1; i < ndigit; i++)
227 			*rs++ = *b++;
228 		*rs++ = 'e';
229 		if (decpt < 0) {
230 			decpt = -decpt;
231 			*rs++ = '-';
232 		} else {
233 			*rs++ = '+';
234 		}
235 		if (decpt >= 100) {
236 			*rs++ = decpt / 100 + '0';
237 			decpt %= 100;
238 		}
239 		*rs++ = decpt / 10 + '0';
240 		*rs++ = decpt % 10 + '0';
241 	} else {				/* f-format */
242 		if (decpt <= 0) {
243 			if (*b != '0') {
244 #ifndef	V7_FLOATSTYLE
245 				*rs++ = '0';
246 #endif
247 				*rs++ = dpoint;	/* '.' */
248 			}
249 			while (decpt < 0) {
250 				decpt++;
251 				*rs++ = '0';
252 			}
253 		}
254 		for (i = 1; i <= ndigit; i++) {
255 			*rs++ = *b++;
256 			if (i == decpt)
257 				*rs++ = dpoint;	/* '.' */
258 		}
259 		if (ndigit < decpt) {
260 			while (ndigit++ < decpt)
261 				*rs++ = '0';
262 			*rs++ = dpoint;		/* '.' */
263 		}
264 	}
265 	if (rs[-1] == dpoint)			/* '.' */
266 		rs--;
267 	*rs = '\0';
268 	return (buf);
269 }
270 #endif
271