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