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