1/*	$OpenBSD: gcvt.c,v 1.11 2009/10/16 12:15:03 martynas Exp $	*/
2
3/*
4 * Copyright (c) 2002, 2003, 2006 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * Sponsored in part by the Defense Advanced Research Projects
19 * Agency (DARPA) and Air Force Research Laboratory, Air Force
20 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
21 */
22
23#include <locale.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28extern char *__dtoa(double, int, int, int *, int *, char **);
29extern void  __freedtoa(char *);
30
31char *
32gcvt(double value, int ndigit, char *buf)
33{
34	char *digits, *dst, *src;
35	int i, decpt, sign;
36	struct lconv *lconv;
37
38	lconv = localeconv();
39	if (ndigit == 0) {
40		buf[0] = '\0';
41		return (buf);
42	}
43
44	digits = __dtoa(value, 2, ndigit, &decpt, &sign, NULL);
45	if (digits == NULL)
46		return (NULL);
47	if (decpt == 9999) {
48		/*
49		 * Infinity or NaN, convert to inf or nan with sign.
50		 * We assume the buffer is at least ndigit long.
51		 */
52		snprintf(buf, ndigit + 1, "%s%s", sign ? "-" : "",
53		    *digits == 'I' ? "inf" : "nan");
54		__freedtoa(digits);
55		return (buf);
56	}
57
58	dst = buf;
59	if (sign)
60		*dst++ = '-';
61
62	if (decpt < 0 || decpt > ndigit) {
63		/* exponential format (e.g. 1.2345e+13) */
64		if (--decpt < 0) {
65			sign = 1;
66			decpt = -decpt;
67		} else
68			sign = 0;
69		src = digits;
70		*dst++ = *src++;
71		dst = stpcpy(dst, lconv->decimal_point);
72		while (*src != '\0')
73			*dst++ = *src++;
74		*dst++ = 'e';
75		if (sign)
76			*dst++ = '-';
77		else
78			*dst++ = '+';
79		if (decpt < 10) {
80			*dst++ = '0';
81			*dst++ = '0' + decpt;
82			*dst = '\0';
83		} else {
84			/* XXX - optimize */
85			for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
86				continue;
87			dst[i + 1] = '\0';
88			while (decpt != 0) {
89				dst[i--] = '0' + decpt % 10;
90				decpt /= 10;
91			}
92		}
93	} else {
94		/* standard format */
95		for (i = 0, src = digits; i < decpt; i++) {
96			if (*src != '\0')
97				*dst++ = *src++;
98			else
99				*dst++ = '0';
100		}
101		if (*src != '\0') {
102			if (src == digits)
103				*dst++ = '0';	/* zero before decimal point */
104			dst = stpcpy(dst, lconv->decimal_point);
105			for (i = decpt; digits[i] != '\0'; i++) {
106				*dst++ = digits[i];
107			}
108		}
109		*dst = '\0';
110	}
111	__freedtoa(digits);
112	return (buf);
113}
114