1/*
2 * Copyright (c) 1988, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/types.h>
31
32#include <ctype.h>
33#include <err.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38
39#define	MAXNUM		65		/* Biggest number we handle. */
40
41static const char	*name1[] = {
42	"",		"one",		"two",		"three",
43	"four",		"five",		"six",		"seven",
44	"eight",	"nine",		"ten",		"eleven",
45	"twelve",	"thirteen",	"fourteen",	"fifteen",
46	"sixteen",	"seventeen",	"eighteen",	"nineteen",
47},
48		*name2[] = {
49	"",		"ten",		"twenty",	"thirty",
50	"forty",	"fifty",	"sixty",	"seventy",
51	"eighty",	"ninety",
52},
53		*name3[] = {
54	"hundred",	"thousand",	"million",	"billion",
55	"trillion",	"quadrillion",	"quintillion",	"sextillion",
56	"septillion",	"octillion",	"nonillion",	"decillion",
57	"undecillion",	"duodecillion",	"tredecillion",	"quattuordecillion",
58	"quindecillion",		"sexdecillion",
59	"septendecillion",		"octodecillion",
60	"novemdecillion",		"vigintillion",
61};
62
63static void	convert(char *);
64static int	number(char *, int);
65static void	pfract(int);
66static int	unit(int, char *);
67static void	usage(void);
68
69static int lflag;
70
71int
72main(int argc, char *argv[])
73{
74	int ch, first;
75	char line[256];
76
77	lflag = 0;
78	while ((ch = getopt(argc, argv, "l")) != -1)
79		switch (ch) {
80		case 'l':
81			lflag = 1;
82			break;
83		case '?':
84		default:
85			usage();
86		}
87	argc -= optind;
88	argv += optind;
89
90	if (*argv == NULL)
91		for (first = 1;
92		    fgets(line, sizeof(line), stdin) != NULL; first = 0) {
93			if (strchr(line, '\n') == NULL)
94				errx(1, "line too long.");
95			if (!first)
96				(void)printf("...\n");
97			convert(line);
98		}
99	else
100		for (first = 1; *argv != NULL; first = 0, ++argv) {
101			if (!first)
102				(void)printf("...\n");
103			convert(*argv);
104		}
105	exit(0);
106}
107
108static void
109convert(char *line)
110{
111	int flen, len, rval;
112	char *p, *fraction;
113
114	flen = 0;
115	fraction = NULL;
116	for (p = line; *p != '\0' && *p != '\n'; ++p) {
117		if (isblank(*p)) {
118			if (p == line) {
119				++line;
120				continue;
121			}
122			goto badnum;
123		}
124		if (isdigit(*p))
125			continue;
126		switch (*p) {
127		case '.':
128			if (fraction != NULL)
129				goto badnum;
130			fraction = p + 1;
131			*p = '\0';
132			break;
133		case '-':
134			if (p == line)
135				break;
136			/* FALLTHROUGH */
137		default:
138badnum:			errx(1, "illegal number: %s", line);
139			break;
140		}
141	}
142	*p = '\0';
143
144	if ((len = strlen(line)) > MAXNUM ||
145	    (fraction != NULL && ((flen = strlen(fraction)) > MAXNUM)))
146		errx(1, "number too large, max %d digits.", MAXNUM);
147
148	if (*line == '-') {
149		(void)printf("minus%s", lflag ? " " : "\n");
150		++line;
151		--len;
152	}
153
154	rval = len > 0 ? unit(len, line) : 0;
155	if (fraction != NULL && flen != 0)
156		for (p = fraction; *p != '\0'; ++p)
157			if (*p != '0') {
158				if (rval)
159					(void)printf("%sand%s",
160					    lflag ? " " : "",
161					    lflag ? " " : "\n");
162				if (unit(flen, fraction)) {
163					if (lflag)
164						(void)printf(" ");
165					pfract(flen);
166					rval = 1;
167				}
168				break;
169			}
170	if (!rval)
171		(void)printf("zero%s", lflag ? "" : ".\n");
172	if (lflag)
173		(void)printf("\n");
174}
175
176static int
177unit(int len, char *p)
178{
179	int off, rval;
180
181	rval = 0;
182	if (len > 3) {
183		if (len % 3) {
184			off = len % 3;
185			len -= off;
186			if (number(p, off)) {
187				rval = 1;
188				(void)printf(" %s%s",
189				    name3[len / 3], lflag ? " " : ".\n");
190			}
191			p += off;
192		}
193		for (; len > 3; p += 3) {
194			len -= 3;
195			if (number(p, 3)) {
196				rval = 1;
197				(void)printf(" %s%s",
198				    name3[len / 3], lflag ? " " : ".\n");
199			}
200		}
201	}
202	if (number(p, len)) {
203		if (!lflag)
204			(void)printf(".\n");
205		rval = 1;
206	}
207	return (rval);
208}
209
210static int
211number(char *p, int len)
212{
213	int val, rval;
214
215	rval = 0;
216	switch (len) {
217	case 3:
218		if (*p != '0') {
219			rval = 1;
220			(void)printf("%s hundred", name1[*p - '0']);
221		}
222		++p;
223		/* FALLTHROUGH */
224	case 2:
225		val = (p[1] - '0') + (p[0] - '0') * 10;
226		if (val) {
227			if (rval)
228				(void)printf(" ");
229			if (val < 20)
230				(void)printf("%s", name1[val]);
231			else {
232				(void)printf("%s", name2[val / 10]);
233				if (val % 10)
234					(void)printf("-%s", name1[val % 10]);
235			}
236			rval = 1;
237		}
238		break;
239	case 1:
240		if (*p != '0') {
241			rval = 1;
242			(void)printf("%s", name1[*p - '0']);
243		}
244	}
245	return (rval);
246}
247
248static void
249pfract(int len)
250{
251	static char const * const pref[] = { "", "ten-", "hundred-" };
252
253	switch(len) {
254	case 1:
255		(void)printf("tenths.\n");
256		break;
257	case 2:
258		(void)printf("hundredths.\n");
259		break;
260	default:
261		(void)printf("%s%sths.\n", pref[len % 3], name3[len / 3]);
262		break;
263	}
264}
265
266static void
267usage(void)
268{
269	(void)fprintf(stderr, "usage: number [-l] [# ...]\n");
270	exit(1);
271}
272