12490Sjkh/*
22490Sjkh * Copyright (c) 1988, 1993, 1994
32490Sjkh *	The Regents of the University of California.  All rights reserved.
42490Sjkh *
52490Sjkh * Redistribution and use in source and binary forms, with or without
62490Sjkh * modification, are permitted provided that the following conditions
72490Sjkh * are met:
82490Sjkh * 1. Redistributions of source code must retain the above copyright
92490Sjkh *    notice, this list of conditions and the following disclaimer.
102490Sjkh * 2. Redistributions in binary form must reproduce the above copyright
112490Sjkh *    notice, this list of conditions and the following disclaimer in the
122490Sjkh *    documentation and/or other materials provided with the distribution.
13203932Simp * 3. Neither the name of the University nor the names of its contributors
142490Sjkh *    may be used to endorse or promote products derived from this software
152490Sjkh *    without specific prior written permission.
162490Sjkh *
172490Sjkh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
182490Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
192490Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202490Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
212490Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
222490Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
232490Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
242490Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
252490Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
262490Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
272490Sjkh * SUCH DAMAGE.
282490Sjkh */
292490Sjkh
302490Sjkh#ifndef lint
3153920Sbillfstatic const char copyright[] =
322490Sjkh"@(#) Copyright (c) 1988, 1993, 1994\n\
332490Sjkh	The Regents of the University of California.  All rights reserved.\n";
342490Sjkh#endif /* not lint */
352490Sjkh
362490Sjkh#ifndef lint
3753920Sbillf#if 0
3823726Speterstatic char sccsid[] = "@(#)number.c	8.3 (Berkeley) 5/4/95";
3953920Sbillf#endif
4053920Sbillfstatic const char rcsid[] =
4153920Sbillf "$FreeBSD$";
422490Sjkh#endif /* not lint */
432490Sjkh
442490Sjkh#include <sys/types.h>
452490Sjkh
462490Sjkh#include <ctype.h>
4723726Speter#include <err.h>
482490Sjkh#include <stdio.h>
492490Sjkh#include <stdlib.h>
502490Sjkh#include <string.h>
5123726Speter#include <unistd.h>
522490Sjkh
532490Sjkh#define	MAXNUM		65		/* Biggest number we handle. */
542490Sjkh
5554468Sbillfstatic const char	*name1[] = {
562490Sjkh	"",		"one",		"two",		"three",
572490Sjkh	"four",		"five",		"six",		"seven",
582490Sjkh	"eight",	"nine",		"ten",		"eleven",
592490Sjkh	"twelve",	"thirteen",	"fourteen",	"fifteen",
602490Sjkh	"sixteen",	"seventeen",	"eighteen",	"nineteen",
612490Sjkh},
622490Sjkh		*name2[] = {
632490Sjkh	"",		"ten",		"twenty",	"thirty",
642490Sjkh	"forty",	"fifty",	"sixty",	"seventy",
652490Sjkh	"eighty",	"ninety",
662490Sjkh},
672490Sjkh		*name3[] = {
682490Sjkh	"hundred",	"thousand",	"million",	"billion",
692490Sjkh	"trillion",	"quadrillion",	"quintillion",	"sextillion",
702490Sjkh	"septillion",	"octillion",	"nonillion",	"decillion",
712490Sjkh	"undecillion",	"duodecillion",	"tredecillion",	"quattuordecillion",
728856Srgrimes	"quindecillion",		"sexdecillion",
732490Sjkh	"septendecillion",		"octodecillion",
742490Sjkh	"novemdecillion",		"vigintillion",
752490Sjkh};
762490Sjkh
77227101Sedstatic void	convert(char *);
78227101Sedstatic int	number(char *, int);
79227101Sedstatic void	pfract(int);
80227101Sedstatic int	unit(int, char *);
81227101Sedstatic void	usage(void);
822490Sjkh
83227101Sedstatic int lflag;
842490Sjkh
852490Sjkhint
86198021Sedmain(int argc, char *argv[])
872490Sjkh{
882490Sjkh	int ch, first;
892490Sjkh	char line[256];
902490Sjkh
912490Sjkh	lflag = 0;
9233937Sjkh	while ((ch = getopt(argc, argv, "l")) != -1)
932490Sjkh		switch (ch) {
942490Sjkh		case 'l':
952490Sjkh			lflag = 1;
962490Sjkh			break;
972490Sjkh		case '?':
982490Sjkh		default:
992490Sjkh			usage();
1002490Sjkh		}
1012490Sjkh	argc -= optind;
1022490Sjkh	argv += optind;
1032490Sjkh
1042490Sjkh	if (*argv == NULL)
1052490Sjkh		for (first = 1;
1062490Sjkh		    fgets(line, sizeof(line), stdin) != NULL; first = 0) {
1072490Sjkh			if (strchr(line, '\n') == NULL)
1082490Sjkh				errx(1, "line too long.");
1092490Sjkh			if (!first)
1102490Sjkh				(void)printf("...\n");
1112490Sjkh			convert(line);
1122490Sjkh		}
1132490Sjkh	else
1142490Sjkh		for (first = 1; *argv != NULL; first = 0, ++argv) {
1152490Sjkh			if (!first)
1162490Sjkh				(void)printf("...\n");
1172490Sjkh			convert(*argv);
1182490Sjkh		}
1192490Sjkh	exit(0);
1202490Sjkh}
1212490Sjkh
122227101Sedstatic void
123201176Sedconvert(char *line)
1242490Sjkh{
12553215Smarcel	int flen, len, rval;
12653210Sbillf	char *p, *fraction;
1272490Sjkh
128126954Sbde	flen = 0;
1292490Sjkh	fraction = NULL;
1302490Sjkh	for (p = line; *p != '\0' && *p != '\n'; ++p) {
1312490Sjkh		if (isblank(*p)) {
1322490Sjkh			if (p == line) {
1332490Sjkh				++line;
1342490Sjkh				continue;
1352490Sjkh			}
1362490Sjkh			goto badnum;
1372490Sjkh		}
1382490Sjkh		if (isdigit(*p))
1392490Sjkh			continue;
1402490Sjkh		switch (*p) {
1412490Sjkh		case '.':
1422490Sjkh			if (fraction != NULL)
1432490Sjkh				goto badnum;
1442490Sjkh			fraction = p + 1;
1452490Sjkh			*p = '\0';
1462490Sjkh			break;
1472490Sjkh		case '-':
1482490Sjkh			if (p == line)
1492490Sjkh				break;
1502490Sjkh			/* FALLTHROUGH */
1512490Sjkh		default:
1522490Sjkhbadnum:			errx(1, "illegal number: %s", line);
1532490Sjkh			break;
1542490Sjkh		}
1552490Sjkh	}
1562490Sjkh	*p = '\0';
1572490Sjkh
1582490Sjkh	if ((len = strlen(line)) > MAXNUM ||
15954468Sbillf	    (fraction != NULL && ((flen = strlen(fraction)) > MAXNUM)))
1602490Sjkh		errx(1, "number too large, max %d digits.", MAXNUM);
1612490Sjkh
1622490Sjkh	if (*line == '-') {
1632490Sjkh		(void)printf("minus%s", lflag ? " " : "\n");
1642490Sjkh		++line;
16533856Ssteve		--len;
1662490Sjkh	}
1672490Sjkh
1682490Sjkh	rval = len > 0 ? unit(len, line) : 0;
1692490Sjkh	if (fraction != NULL && flen != 0)
1702490Sjkh		for (p = fraction; *p != '\0'; ++p)
1712490Sjkh			if (*p != '0') {
1722490Sjkh				if (rval)
1732490Sjkh					(void)printf("%sand%s",
1742490Sjkh					    lflag ? " " : "",
1752490Sjkh					    lflag ? " " : "\n");
1762490Sjkh				if (unit(flen, fraction)) {
1772490Sjkh					if (lflag)
1782490Sjkh						(void)printf(" ");
1792490Sjkh					pfract(flen);
1802490Sjkh					rval = 1;
1812490Sjkh				}
1822490Sjkh				break;
1832490Sjkh			}
1842490Sjkh	if (!rval)
1852490Sjkh		(void)printf("zero%s", lflag ? "" : ".\n");
1862490Sjkh	if (lflag)
1872490Sjkh		(void)printf("\n");
1882490Sjkh}
1892490Sjkh
190227101Sedstatic int
191201176Sedunit(int len, char *p)
1922490Sjkh{
19353210Sbillf	int off, rval;
1942490Sjkh
1952490Sjkh	rval = 0;
1962490Sjkh	if (len > 3) {
1972490Sjkh		if (len % 3) {
1982490Sjkh			off = len % 3;
1992490Sjkh			len -= off;
2002490Sjkh			if (number(p, off)) {
2012490Sjkh				rval = 1;
2022490Sjkh				(void)printf(" %s%s",
2032490Sjkh				    name3[len / 3], lflag ? " " : ".\n");
2042490Sjkh			}
2052490Sjkh			p += off;
2062490Sjkh		}
2072490Sjkh		for (; len > 3; p += 3) {
2082490Sjkh			len -= 3;
2092490Sjkh			if (number(p, 3)) {
2102490Sjkh				rval = 1;
2112490Sjkh				(void)printf(" %s%s",
2122490Sjkh				    name3[len / 3], lflag ? " " : ".\n");
2132490Sjkh			}
2142490Sjkh		}
2152490Sjkh	}
2162490Sjkh	if (number(p, len)) {
2172490Sjkh		if (!lflag)
2182490Sjkh			(void)printf(".\n");
2192490Sjkh		rval = 1;
2202490Sjkh	}
2212490Sjkh	return (rval);
2222490Sjkh}
2232490Sjkh
224227101Sedstatic int
225201176Sednumber(char *p, int len)
2262490Sjkh{
22753210Sbillf	int val, rval;
2282490Sjkh
2292490Sjkh	rval = 0;
2302490Sjkh	switch (len) {
2312490Sjkh	case 3:
2322490Sjkh		if (*p != '0') {
2332490Sjkh			rval = 1;
2342490Sjkh			(void)printf("%s hundred", name1[*p - '0']);
2352490Sjkh		}
2362490Sjkh		++p;
2372490Sjkh		/* FALLTHROUGH */
2382490Sjkh	case 2:
2392490Sjkh		val = (p[1] - '0') + (p[0] - '0') * 10;
2402490Sjkh		if (val) {
2412490Sjkh			if (rval)
2422490Sjkh				(void)printf(" ");
2432490Sjkh			if (val < 20)
2442490Sjkh				(void)printf("%s", name1[val]);
2452490Sjkh			else {
2462490Sjkh				(void)printf("%s", name2[val / 10]);
2472490Sjkh				if (val % 10)
2482490Sjkh					(void)printf("-%s", name1[val % 10]);
2492490Sjkh			}
2502490Sjkh			rval = 1;
2512490Sjkh		}
2522490Sjkh		break;
2532490Sjkh	case 1:
2542490Sjkh		if (*p != '0') {
2552490Sjkh			rval = 1;
2562490Sjkh			(void)printf("%s", name1[*p - '0']);
2572490Sjkh		}
2582490Sjkh	}
2592490Sjkh	return (rval);
2602490Sjkh}
2612490Sjkh
262227101Sedstatic void
263201176Sedpfract(int len)
2642490Sjkh{
265198021Sed	static char const * const pref[] = { "", "ten-", "hundred-" };
2662490Sjkh
2672490Sjkh	switch(len) {
2682490Sjkh	case 1:
2692490Sjkh		(void)printf("tenths.\n");
2702490Sjkh		break;
2712490Sjkh	case 2:
2722490Sjkh		(void)printf("hundredths.\n");
2732490Sjkh		break;
2742490Sjkh	default:
2752490Sjkh		(void)printf("%s%sths.\n", pref[len % 3], name3[len / 3]);
2762490Sjkh		break;
2772490Sjkh	}
2782490Sjkh}
2792490Sjkh
280227101Sedstatic void
281201176Sedusage(void)
2822490Sjkh{
283141581Sru	(void)fprintf(stderr, "usage: number [-l] [# ...]\n");
2842490Sjkh	exit(1);
2852490Sjkh}
286