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