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