xref: /openbsd/games/number/number.c (revision f2dfb0a4)
1 /*	$OpenBSD: number.c,v 1.6 1998/03/25 08:45:25 pjanzen Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1988, 1993, 1994\n\
39 	The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)number.c	8.2 (Berkeley) 3/31/94";
45 #else
46 static char rcsid[] = "$OpenBSD: number.c,v 1.6 1998/03/25 08:45:25 pjanzen Exp $";
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/types.h>
51 
52 #include <ctype.h>
53 #include <err.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #define	MAXNUM		65		/* Biggest number we handle. */
60 #define	LINELEN		256
61 
62 static char	*name1[] = {
63 	"",		"one",		"two",		"three",
64 	"four",		"five",		"six",		"seven",
65 	"eight",	"nine",		"ten",		"eleven",
66 	"twelve",	"thirteen",	"fourteen",	"fifteen",
67 	"sixteen",	"seventeen",	"eighteen",	"nineteen",
68 },
69 		*name2[] = {
70 	"",		"ten",		"twenty",	"thirty",
71 	"forty",	"fifty",	"sixty",	"seventy",
72 	"eighty",	"ninety",
73 },
74 		*name3[] = {
75 	"hundred",	"thousand",	"million",	"billion",
76 	"trillion",	"quadrillion",	"quintillion",	"sextillion",
77 	"septillion",	"octillion",	"nonillion",	"decillion",
78 	"undecillion",	"duodecillion",	"tredecillion",	"quattuordecillion",
79 	"quindecillion",		"sexdecillion",
80 	"septendecillion",		"octodecillion",
81 	"novemdecillion",		"vigintillion",
82 };
83 
84 void	convert __P((char *));
85 void	convertexp __P((char *));
86 int	number __P((char *, int));
87 void	pfract __P((int));
88 void	toobig __P((void));
89 int	unit __P((int, char *));
90 void	usage __P((void));
91 
92 int lflag;
93 
94 int
95 main(argc, argv)
96 	int argc;
97 	char *argv[];
98 {
99 	int ch, first;
100 	char line[LINELEN];
101 
102 	/* revoke */
103 	setegid(getgid());
104 	setgid(getgid());
105 
106 	lflag = 0;
107 	while ((ch = getopt(argc, argv, "hl")) != -1)
108 		switch (ch) {
109 		case 'l':
110 			lflag = 1;
111 			break;
112 		case '?':
113 		case 'h':
114 		default:
115 			usage();
116 		}
117 	argc -= optind;
118 	argv += optind;
119 
120 	if (*argv == NULL)
121 		for (first = 1;
122 		    fgets(line, sizeof(line), stdin) != NULL; first = 0) {
123 			if (strchr(line, '\n') == NULL)
124 				errx(1, "line too long.");
125 			if (!first)
126 				(void)printf("...\n");
127 			convert(line);
128 			if (lflag)
129 				(void)printf("\n");
130 		}
131 	else
132 		for (first = 1; *argv != NULL; first = 0, ++argv) {
133 			if (!first)
134 				(void)printf("...\n");
135 			convert(*argv);
136 			if (lflag)
137 				(void)printf("\n");
138 		}
139 	exit(0);
140 }
141 
142 void
143 convert(line)
144 	char *line;
145 {
146 	int flen, len, rval;
147 	char *p, *fraction;
148 
149 	/* strip trailing and leading whitespace */
150 	len = strlen(line) - 1;
151 	while ((isblank(line[len])) || (line[len] == '\n'))
152 		line[len--] = '\0';
153 	while ((isblank(line[0])) || (line[0] == '\n'))
154 		line++;
155 	if (strchr(line, 'e') || strchr(line, 'E'))
156 		convertexp(line);
157 	else {
158 	fraction = NULL;
159 	for (p = line; *p != '\0' && *p != '\n'; ++p) {
160 		if (isblank(*p))
161 			goto badnum;
162 		if (isdigit(*p))
163 			continue;
164 		switch (*p) {
165 		case '.':
166 			if (fraction != NULL)
167 				goto badnum;
168 			fraction = p + 1;
169 			*p = '\0';
170 			break;
171 		case '-':
172 		case '+':
173 			if (p == line)
174 				break;
175 			/* FALLTHROUGH */
176 		default:
177 badnum:			errx(1, "illegal number: %s", line);
178 			break;
179 		}
180 	}
181 	*p = '\0';
182 
183 	if ((len = strlen(line)) > MAXNUM ||
184 	    ((fraction != NULL) && (flen = strlen(fraction))) > MAXNUM)
185 		errx(1, "number too large (max %d digits).", MAXNUM);
186 
187 	if (*line == '-') {
188 		(void)printf("minus%s", lflag ? " " : "\n");
189 		++line;
190 		--len;
191 	}
192 	if (*line == '+') {
193 		(void)printf("plus%s", lflag ? " " : "\n");
194 		++line;
195 		--len;
196 	}
197 
198 	rval = len > 0 ? unit(len, line) : 0;
199 	if (fraction != NULL && flen != 0)
200 		for (p = fraction; *p != '\0'; ++p)
201 			if (*p != '0') {
202 				if (rval)
203 					(void)printf("%sand%s",
204 					    lflag ? " " : "",
205 					    lflag ? " " : "\n");
206 				if (unit(flen, fraction)) {
207 					if (lflag)
208 						(void)printf(" ");
209 					pfract(flen);
210 					rval = 1;
211 				}
212 				break;
213 			}
214 	if (!rval)
215 		(void)printf("zero%s", lflag ? "" : ".\n");
216 	}
217 }
218 
219 void
220 convertexp(line)
221 	char *line;
222 {
223 	char locline[LINELEN];
224 	char *part1, *part2, *part3, *part4;
225 	char tmp[2];
226 	int  i, j;
227 
228 	(void)strncpy(locline,line,LINELEN);
229 	part3 = locline;
230 	part2 = strsep(&part3, "eE");	/* part3 is the exponent */
231 	part4 = part3;
232 	(void)strsep(&part4, ".");	/* no decimal allowed in the exponent */
233 	if (part4)
234 		errx(1, "illegal number: %s", line);
235 	part1 = strsep(&part2, ".");	/* we can have one in the mantissa */
236 	/* At this point everything should be null or a digit.  Check for
237 	 * that before starting to convert.  Two characters may be + or -.
238 	 */
239 	j = strlen(line);
240 	for (i = 0; i < j; i++)
241 		if ((!isdigit(locline[i])) && (locline[i]))
242 			if (((locline[i] != '+') && (locline[i] != '-')) ||
243 				((i != 0) && (i != part3 - locline)))
244 				errx(1, "illegal number: %s", line);
245 	convert(part1);
246 	printf("%s", lflag ? " " : "");
247 	if (part2 && part2[0]) {	/* do individual digits separately */
248 		(void)printf("point%s", lflag ? " " : "\n");
249 		j = strlen(part2); tmp[1] = '\0';
250 		for (i = 0 ; i < j; i++ ) {
251 			tmp[0] = part2[i];
252 			convert(tmp);
253 			(void)printf("%s", lflag ? " " : "");
254 		}
255 	}
256 	(void)printf("times ten to the%s", lflag ? " " : "\n");
257 	if (part3 && part3[0])
258 		convert(part3);
259 	else
260 		(void)printf("zero%s", lflag ? " " : ".\n");
261 }
262 
263 int
264 unit(len, p)
265 	register int len;
266 	register char *p;
267 {
268 	register int off, rval;
269 
270 	rval = 0;
271 	if (len > 3) {
272 		if (len % 3) {
273 			off = len % 3;
274 			len -= off;
275 			if (number(p, off)) {
276 				rval = 1;
277 				(void)printf(" %s%s",
278 				    name3[len / 3], lflag ? " " : ".\n");
279 			}
280 			p += off;
281 		}
282 		for (; len > 3; p += 3) {
283 			len -= 3;
284 			if (number(p, 3)) {
285 				rval = 1;
286 				(void)printf(" %s%s",
287 				    name3[len / 3], lflag ? " " : ".\n");
288 			}
289 		}
290 	}
291 	if (number(p, len)) {
292 		if (!lflag)
293 			(void)printf(".\n");
294 		rval = 1;
295 	}
296 	return (rval);
297 }
298 
299 int
300 number(p, len)
301 	register char *p;
302 	int len;
303 {
304 	register int val, rval;
305 
306 	rval = 0;
307 	switch (len) {
308 	case 3:
309 		if (*p != '0') {
310 			rval = 1;
311 			(void)printf("%s hundred", name1[*p - '0']);
312 		}
313 		++p;
314 		/* FALLTHROUGH */
315 	case 2:
316 		val = (p[1] - '0') + (p[0] - '0') * 10;
317 		if (val) {
318 			if (rval)
319 				(void)printf(" ");
320 			if (val < 20)
321 				(void)printf("%s", name1[val]);
322 			else {
323 				(void)printf("%s", name2[val / 10]);
324 				if (val % 10)
325 					(void)printf("-%s", name1[val % 10]);
326 			}
327 			rval = 1;
328 		}
329 		break;
330 	case 1:
331 		if (*p != '0') {
332 			rval = 1;
333 			(void)printf("%s", name1[*p - '0']);
334 		}
335 	}
336 	return (rval);
337 }
338 
339 void
340 pfract(len)
341 	int len;
342 {
343 	static char *pref[] = { "", "ten-", "hundred-" };
344 
345 	switch(len) {
346 	case 1:
347 		(void)printf("tenths.\n");
348 		break;
349 	case 2:
350 		(void)printf("hundredths.\n");
351 		break;
352 	default:
353 		(void)printf("%s%sths.\n", pref[len % 3], name3[len / 3]);
354 		break;
355 	}
356 }
357 
358 void
359 usage()
360 {
361 	(void)fprintf(stderr, "usage: number [-l] [--] [# ...]\n");
362 	exit(1);
363 }
364