number.c revision 141581
1177633Sdfr/*
2177633Sdfr * Copyright (c) 1988, 1993, 1994
3177633Sdfr *	The Regents of the University of California.  All rights reserved.
4177633Sdfr *
5177633Sdfr * Redistribution and use in source and binary forms, with or without
6177633Sdfr * modification, are permitted provided that the following conditions
7177633Sdfr * are met:
8177633Sdfr * 1. Redistributions of source code must retain the above copyright
9177633Sdfr *    notice, this list of conditions and the following disclaimer.
10177633Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11177633Sdfr *    notice, this list of conditions and the following disclaimer in the
12177633Sdfr *    documentation and/or other materials provided with the distribution.
13177633Sdfr * 3. All advertising materials mentioning features or use of this software
14177633Sdfr *    must display the following acknowledgement:
15177633Sdfr *	This product includes software developed by the University of
16177633Sdfr *	California, Berkeley and its contributors.
17177633Sdfr * 4. Neither the name of the University nor the names of its contributors
18177633Sdfr *    may be used to endorse or promote products derived from this software
19177633Sdfr *    without specific prior written permission.
20177633Sdfr *
21177633Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22177633Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23177633Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24177633Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25177633Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26177633Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27177633Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28177633Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29177633Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30177633Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31177633Sdfr * SUCH DAMAGE.
32177633Sdfr */
33177633Sdfr
34177633Sdfr#ifndef lint
35177633Sdfrstatic const char copyright[] =
36177633Sdfr"@(#) Copyright (c) 1988, 1993, 1994\n\
37177633Sdfr	The Regents of the University of California.  All rights reserved.\n";
38177633Sdfr#endif /* not lint */
39177633Sdfr
40177633Sdfr#ifndef lint
41177633Sdfr#if 0
42177633Sdfrstatic char sccsid[] = "@(#)number.c	8.3 (Berkeley) 5/4/95";
43177633Sdfr#endif
44177633Sdfrstatic const char rcsid[] =
45177633Sdfr "$FreeBSD: head/games/number/number.c 141581 2005-02-09 18:22:15Z ru $";
46177633Sdfr#endif /* not lint */
47177633Sdfr
48177633Sdfr#include <sys/types.h>
49177633Sdfr
50177633Sdfr#include <ctype.h>
51177633Sdfr#include <err.h>
52177633Sdfr#include <stdio.h>
53177633Sdfr#include <stdlib.h>
54177633Sdfr#include <string.h>
55184588Sdfr#include <unistd.h>
56184588Sdfr
57184588Sdfr#define	MAXNUM		65		/* Biggest number we handle. */
58184588Sdfr
59184588Sdfrstatic const char	*name1[] = {
60184588Sdfr	"",		"one",		"two",		"three",
61184588Sdfr	"four",		"five",		"six",		"seven",
62177633Sdfr	"eight",	"nine",		"ten",		"eleven",
63177633Sdfr	"twelve",	"thirteen",	"fourteen",	"fifteen",
64177633Sdfr	"sixteen",	"seventeen",	"eighteen",	"nineteen",
65177633Sdfr},
66177633Sdfr		*name2[] = {
67177633Sdfr	"",		"ten",		"twenty",	"thirty",
68177633Sdfr	"forty",	"fifty",	"sixty",	"seventy",
69177633Sdfr	"eighty",	"ninety",
70177633Sdfr},
71177633Sdfr		*name3[] = {
72177633Sdfr	"hundred",	"thousand",	"million",	"billion",
73177633Sdfr	"trillion",	"quadrillion",	"quintillion",	"sextillion",
74177633Sdfr	"septillion",	"octillion",	"nonillion",	"decillion",
75177633Sdfr	"undecillion",	"duodecillion",	"tredecillion",	"quattuordecillion",
76177633Sdfr	"quindecillion",		"sexdecillion",
77177633Sdfr	"septendecillion",		"octodecillion",
78177633Sdfr	"novemdecillion",		"vigintillion",
79177633Sdfr};
80177633Sdfr
81177633Sdfrvoid	convert(char *);
82177633Sdfrint	number(char *, int);
83177633Sdfrvoid	pfract(int);
84177633Sdfrvoid	toobig(void);
85177633Sdfrint	unit(int, char *);
86177633Sdfrvoid	usage(void);
87184588Sdfr
88184588Sdfrint lflag;
89177633Sdfr
90177633Sdfrint
91177633Sdfrmain(argc, argv)
92177633Sdfr	int argc;
93177633Sdfr	char *argv[];
94177633Sdfr{
95177633Sdfr	int ch, first;
96177633Sdfr	char line[256];
97177633Sdfr
98177633Sdfr	lflag = 0;
99177633Sdfr	while ((ch = getopt(argc, argv, "l")) != -1)
100184588Sdfr		switch (ch) {
101184588Sdfr		case 'l':
102184588Sdfr			lflag = 1;
103184588Sdfr			break;
104184588Sdfr		case '?':
105177633Sdfr		default:
106177633Sdfr			usage();
107177633Sdfr		}
108177633Sdfr	argc -= optind;
109177633Sdfr	argv += optind;
110177633Sdfr
111177633Sdfr	if (*argv == NULL)
112184588Sdfr		for (first = 1;
113184588Sdfr		    fgets(line, sizeof(line), stdin) != NULL; first = 0) {
114184588Sdfr			if (strchr(line, '\n') == NULL)
115184588Sdfr				errx(1, "line too long.");
116184588Sdfr			if (!first)
117184588Sdfr				(void)printf("...\n");
118184588Sdfr			convert(line);
119184588Sdfr		}
120184588Sdfr	else
121184588Sdfr		for (first = 1; *argv != NULL; first = 0, ++argv) {
122184588Sdfr			if (!first)
123184588Sdfr				(void)printf("...\n");
124184588Sdfr			convert(*argv);
125184588Sdfr		}
126184588Sdfr	exit(0);
127184588Sdfr}
128184588Sdfr
129184588Sdfrvoid
130184588Sdfrconvert(line)
131184588Sdfr	char *line;
132184588Sdfr{
133184588Sdfr	int flen, len, rval;
134184588Sdfr	char *p, *fraction;
135184588Sdfr
136184588Sdfr	flen = 0;
137184588Sdfr	fraction = NULL;
138184588Sdfr	for (p = line; *p != '\0' && *p != '\n'; ++p) {
139184588Sdfr		if (isblank(*p)) {
140184588Sdfr			if (p == line) {
141184588Sdfr				++line;
142177633Sdfr				continue;
143177633Sdfr			}
144177633Sdfr			goto badnum;
145177633Sdfr		}
146184588Sdfr		if (isdigit(*p))
147184588Sdfr			continue;
148177633Sdfr		switch (*p) {
149177633Sdfr		case '.':
150177633Sdfr			if (fraction != NULL)
151177633Sdfr				goto badnum;
152184588Sdfr			fraction = p + 1;
153184588Sdfr			*p = '\0';
154184588Sdfr			break;
155177633Sdfr		case '-':
156184588Sdfr			if (p == line)
157184588Sdfr				break;
158184588Sdfr			/* FALLTHROUGH */
159184588Sdfr		default:
160184588Sdfrbadnum:			errx(1, "illegal number: %s", line);
161184588Sdfr			break;
162184588Sdfr		}
163184588Sdfr	}
164184588Sdfr	*p = '\0';
165184588Sdfr
166184588Sdfr	if ((len = strlen(line)) > MAXNUM ||
167184588Sdfr	    (fraction != NULL && ((flen = strlen(fraction)) > MAXNUM)))
168177633Sdfr		errx(1, "number too large, max %d digits.", MAXNUM);
169177633Sdfr
170177633Sdfr	if (*line == '-') {
171177633Sdfr		(void)printf("minus%s", lflag ? " " : "\n");
172177633Sdfr		++line;
173177633Sdfr		--len;
174177633Sdfr	}
175177633Sdfr
176177633Sdfr	rval = len > 0 ? unit(len, line) : 0;
177177633Sdfr	if (fraction != NULL && flen != 0)
178184588Sdfr		for (p = fraction; *p != '\0'; ++p)
179177633Sdfr			if (*p != '0') {
180177633Sdfr				if (rval)
181177633Sdfr					(void)printf("%sand%s",
182177633Sdfr					    lflag ? " " : "",
183184588Sdfr					    lflag ? " " : "\n");
184184588Sdfr				if (unit(flen, fraction)) {
185177633Sdfr					if (lflag)
186177633Sdfr						(void)printf(" ");
187184588Sdfr					pfract(flen);
188184588Sdfr					rval = 1;
189184588Sdfr				}
190184588Sdfr				break;
191184588Sdfr			}
192177633Sdfr	if (!rval)
193177633Sdfr		(void)printf("zero%s", lflag ? "" : ".\n");
194177633Sdfr	if (lflag)
195177633Sdfr		(void)printf("\n");
196177633Sdfr}
197
198int
199unit(len, p)
200	int len;
201	char *p;
202{
203	int off, rval;
204
205	rval = 0;
206	if (len > 3) {
207		if (len % 3) {
208			off = len % 3;
209			len -= off;
210			if (number(p, off)) {
211				rval = 1;
212				(void)printf(" %s%s",
213				    name3[len / 3], lflag ? " " : ".\n");
214			}
215			p += off;
216		}
217		for (; len > 3; p += 3) {
218			len -= 3;
219			if (number(p, 3)) {
220				rval = 1;
221				(void)printf(" %s%s",
222				    name3[len / 3], lflag ? " " : ".\n");
223			}
224		}
225	}
226	if (number(p, len)) {
227		if (!lflag)
228			(void)printf(".\n");
229		rval = 1;
230	}
231	return (rval);
232}
233
234int
235number(p, len)
236	char *p;
237	int len;
238{
239	int val, rval;
240
241	rval = 0;
242	switch (len) {
243	case 3:
244		if (*p != '0') {
245			rval = 1;
246			(void)printf("%s hundred", name1[*p - '0']);
247		}
248		++p;
249		/* FALLTHROUGH */
250	case 2:
251		val = (p[1] - '0') + (p[0] - '0') * 10;
252		if (val) {
253			if (rval)
254				(void)printf(" ");
255			if (val < 20)
256				(void)printf("%s", name1[val]);
257			else {
258				(void)printf("%s", name2[val / 10]);
259				if (val % 10)
260					(void)printf("-%s", name1[val % 10]);
261			}
262			rval = 1;
263		}
264		break;
265	case 1:
266		if (*p != '0') {
267			rval = 1;
268			(void)printf("%s", name1[*p - '0']);
269		}
270	}
271	return (rval);
272}
273
274void
275pfract(len)
276	int len;
277{
278	static char *pref[] = { "", "ten-", "hundred-" };
279
280	switch(len) {
281	case 1:
282		(void)printf("tenths.\n");
283		break;
284	case 2:
285		(void)printf("hundredths.\n");
286		break;
287	default:
288		(void)printf("%s%sths.\n", pref[len % 3], name3[len / 3]);
289		break;
290	}
291}
292
293void
294usage()
295{
296	(void)fprintf(stderr, "usage: number [-l] [# ...]\n");
297	exit(1);
298}
299