Annotation of src/usr.bin/units/units.c, Revision 1.21
1.21 ! deraadt 1: /* $OpenBSD: units.c,v 1.20 2013/11/27 00:13:24 deraadt Exp $ */
1.2 deraadt 2: /* $NetBSD: units.c,v 1.6 1996/04/06 06:01:03 thorpej Exp $ */
3:
1.1 deraadt 4: /*
5: * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
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. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: * Disclaimer: This software is provided by the author "as is". The author
15: * shall not be liable for any damages caused in any way by this software.
16: *
17: * I would appreciate (though I do not require) receiving a copy of any
18: * improvements you might make to this program.
19: */
20:
21: #include <ctype.h>
22: #include <stdio.h>
23: #include <string.h>
24: #include <stdlib.h>
1.19 okan 25: #include <unistd.h>
1.21 ! deraadt 26: #include <err.h>
1.1 deraadt 27:
1.16 jmc 28: #define UNITSFILE "/usr/share/misc/units.lib"
1.1 deraadt 29:
30: #define VERSION "1.0"
31:
32: #define MAXUNITS 1000
1.15 jmc 33: #define MAXPREFIXES 100
1.1 deraadt 34:
35: #define MAXSUBUNITS 500
36:
37: #define PRIMITIVECHAR '!'
38:
39: char *powerstring = "^";
40:
41: struct {
42: char *uname;
43: char *uval;
1.7 deraadt 44: } unittable[MAXUNITS];
1.1 deraadt 45:
46: struct unittype {
47: char *numerator[MAXSUBUNITS];
48: char *denominator[MAXSUBUNITS];
49: double factor;
50: };
51:
52: struct {
53: char *prefixname;
54: char *prefixval;
1.7 deraadt 55: } prefixtable[MAXPREFIXES];
1.1 deraadt 56:
57:
58: char *NULLUNIT = "";
59:
60: int unitcount;
61: int prefixcount;
62:
1.9 deraadt 63: char *dupstr(char *);
64: void readunits(char *);
65: void initializeunit(struct unittype *);
66: int addsubunit(char *[], char *);
67: void showunit(struct unittype *);
68: void zeroerror(void);
69: int addunit(struct unittype *, char *, int);
70: int compare(const void *, const void *);
71: void sortunit(struct unittype *);
72: void cancelunit(struct unittype *);
73: char *lookupunit(char *);
74: int reduceproduct(struct unittype *, int);
75: int reduceunit(struct unittype *);
76: int compareproducts(char **, char **);
77: int compareunits(struct unittype *, struct unittype *);
78: int completereduce(struct unittype *);
79: void showanswer(struct unittype *, struct unittype *);
80: void usage(void);
1.1 deraadt 81:
82: char *
83: dupstr(char *str)
84: {
85: char *ret;
86:
1.7 deraadt 87: ret = strdup(str);
1.1 deraadt 88: if (!ret) {
89: fprintf(stderr, "Memory allocation error\n");
90: exit(3);
91: }
92: return (ret);
93: }
94:
95:
1.9 deraadt 96: void
1.1 deraadt 97: readunits(char *userfile)
98: {
1.15 jmc 99: char line[512], *lineptr;
1.1 deraadt 100: int len, linenum, i;
1.9 deraadt 101: FILE *unitfile;
1.1 deraadt 102:
103: unitcount = 0;
104: linenum = 0;
105:
106: if (userfile) {
1.15 jmc 107: unitfile = fopen(userfile, "r");
1.1 deraadt 108: if (!unitfile) {
109: fprintf(stderr, "Unable to open units file '%s'\n",
110: userfile);
111: exit(1);
112: }
1.4 deraadt 113: } else {
1.15 jmc 114: unitfile = fopen(UNITSFILE, "r");
1.1 deraadt 115: if (!unitfile) {
1.17 jmc 116: fprintf(stderr, "Can't find units file '%s'\n",
117: UNITSFILE);
118: exit(1);
1.1 deraadt 119: }
120: }
121: while (!feof(unitfile)) {
1.12 ray 122: if (!fgets(line, sizeof(line), unitfile))
1.1 deraadt 123: break;
124: linenum++;
125: lineptr = line;
126: if (*lineptr == '/')
127: continue;
128: lineptr += strspn(lineptr, " \n\t");
129: len = strcspn(lineptr, " \n\t");
130: lineptr[len] = 0;
131: if (!strlen(lineptr))
132: continue;
133: if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
134: if (prefixcount == MAXPREFIXES) {
1.4 deraadt 135: fprintf(stderr,
136: "Memory for prefixes exceeded in line %d\n",
1.1 deraadt 137: linenum);
138: continue;
139: }
1.11 pat 140:
1.1 deraadt 141: lineptr[strlen(lineptr) - 1] = 0;
1.11 pat 142: for (i = 0; i < prefixcount; i++) {
143: if (!strcmp(prefixtable[i].prefixname, lineptr))
144: break;
145: }
146: if (i < prefixcount) {
147: fprintf(stderr, "Redefinition of prefix '%s' "
148: "on line %d ignored\n", lineptr, linenum);
149: continue; /* skip duplicate prefix */
150: }
151:
1.1 deraadt 152: prefixtable[prefixcount].prefixname = dupstr(lineptr);
153: lineptr += len + 1;
1.15 jmc 154: lineptr += strspn(lineptr, " \n\t");
155: len = strcspn(lineptr, "\n\t");
156: if (len == 0) {
157: fprintf(stderr, "Unexpected end of prefix on "
158: "line %d\n", linenum);
1.10 pat 159: free(prefixtable[prefixcount].prefixname);
1.1 deraadt 160: continue;
161: }
162: lineptr[len] = 0;
163: prefixtable[prefixcount++].prefixval = dupstr(lineptr);
1.9 deraadt 164: } else { /* it's not a prefix */
1.1 deraadt 165: if (unitcount == MAXUNITS) {
1.4 deraadt 166: fprintf(stderr,
167: "Memory for units exceeded in line %d\n",
1.1 deraadt 168: linenum);
169: continue;
170: }
1.11 pat 171:
172: for (i = 0; i < unitcount; i++) {
173: if (!strcmp(unittable[i].uname, lineptr))
174: break;
175: }
176: if (i < unitcount) {
177: fprintf(stderr, "Redefinition of unit '%s' "
178: "on line %d ignored\n", lineptr, linenum);
179: continue; /* skip duplicate unit */
180: }
181:
1.1 deraadt 182: unittable[unitcount].uname = dupstr(lineptr);
183: lineptr += len + 1;
184: lineptr += strspn(lineptr, " \n\t");
185: if (!strlen(lineptr)) {
1.15 jmc 186: fprintf(stderr, "Unexpected end of unit on "
187: "line %d\n", linenum);
1.10 pat 188: free(unittable[unitcount].uname);
1.1 deraadt 189: continue;
190: }
191: len = strcspn(lineptr, "\n\t");
192: lineptr[len] = 0;
193: unittable[unitcount++].uval = dupstr(lineptr);
194: }
195: }
196: fclose(unitfile);
197: }
198:
1.9 deraadt 199: void
200: initializeunit(struct unittype *theunit)
1.1 deraadt 201: {
202: theunit->factor = 1.0;
203: theunit->numerator[0] = theunit->denominator[0] = NULL;
204: }
205:
206:
1.9 deraadt 207: int
1.1 deraadt 208: addsubunit(char *product[], char *toadd)
209: {
210: char **ptr;
211:
212: for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
213: if (ptr >= product + MAXSUBUNITS) {
214: fprintf(stderr, "Memory overflow in unit reduction\n");
215: return 1;
216: }
217: if (!*ptr)
218: *(ptr + 1) = 0;
219: *ptr = dupstr(toadd);
220: return 0;
221: }
222:
223:
1.9 deraadt 224: void
225: showunit(struct unittype *theunit)
1.1 deraadt 226: {
227: char **ptr;
228: int printedslash;
229: int counter = 1;
230:
231: printf("\t%.8g", theunit->factor);
232: for (ptr = theunit->numerator; *ptr; ptr++) {
233: if (ptr > theunit->numerator && **ptr &&
234: !strcmp(*ptr, *(ptr - 1)))
235: counter++;
236: else {
237: if (counter > 1)
238: printf("%s%d", powerstring, counter);
239: if (**ptr)
240: printf(" %s", *ptr);
241: counter = 1;
242: }
243: }
244: if (counter > 1)
245: printf("%s%d", powerstring, counter);
246: counter = 1;
247: printedslash = 0;
248: for (ptr = theunit->denominator; *ptr; ptr++) {
249: if (ptr > theunit->denominator && **ptr &&
250: !strcmp(*ptr, *(ptr - 1)))
251: counter++;
252: else {
253: if (counter > 1)
254: printf("%s%d", powerstring, counter);
255: if (**ptr) {
256: if (!printedslash)
257: printf(" /");
258: printedslash = 1;
259: printf(" %s", *ptr);
260: }
261: counter = 1;
262: }
263: }
264: if (counter > 1)
265: printf("%s%d", powerstring, counter);
266: printf("\n");
267: }
268:
269:
1.9 deraadt 270: void
1.8 deraadt 271: zeroerror(void)
1.1 deraadt 272: {
273: fprintf(stderr, "Unit reduces to zero\n");
274: }
275:
276: /*
277: Adds the specified string to the unit.
278: Flip is 0 for adding normally, 1 for adding reciprocal.
279:
280: Returns 0 for successful addition, nonzero on error.
281: */
282:
1.9 deraadt 283: int
284: addunit(struct unittype *theunit, char *toadd, int flip)
1.1 deraadt 285: {
286: char *scratch, *savescr;
287: char *item;
288: char *divider, *slash;
289: int doingtop;
290:
291: savescr = scratch = dupstr(toadd);
292: for (slash = scratch + 1; *slash; slash++)
293: if (*slash == '-' &&
1.20 deraadt 294: (tolower((unsigned char)*(slash - 1)) != 'e' ||
1.1 deraadt 295: !strchr(".0123456789", *(slash + 1))))
296: *slash = ' ';
297: slash = strchr(scratch, '/');
298: if (slash)
299: *slash = 0;
300: doingtop = 1;
301: do {
302: item = strtok(scratch, " *\t\n/");
303: while (item) {
304: if (strchr("0123456789.", *item)) { /* item is a number */
305: double num;
306:
307: divider = strchr(item, '|');
308: if (divider) {
309: *divider = 0;
310: num = atof(item);
311: if (!num) {
312: zeroerror();
1.10 pat 313: free(savescr);
1.1 deraadt 314: return 1;
315: }
316: if (doingtop ^ flip)
317: theunit->factor *= num;
318: else
319: theunit->factor /= num;
320: num = atof(divider + 1);
321: if (!num) {
322: zeroerror();
1.10 pat 323: free(savescr);
1.1 deraadt 324: return 1;
325: }
326: if (doingtop ^ flip)
327: theunit->factor /= num;
328: else
329: theunit->factor *= num;
1.9 deraadt 330: } else {
1.1 deraadt 331: num = atof(item);
332: if (!num) {
333: zeroerror();
1.10 pat 334: free(savescr);
1.1 deraadt 335: return 1;
336: }
337: if (doingtop ^ flip)
338: theunit->factor *= num;
339: else
340: theunit->factor /= num;
341:
342: }
1.9 deraadt 343: } else { /* item is not a number */
1.1 deraadt 344: int repeat = 1;
345:
346: if (strchr("23456789",
347: item[strlen(item) - 1])) {
348: repeat = item[strlen(item) - 1] - '0';
349: item[strlen(item) - 1] = 0;
350: }
351: for (; repeat; repeat--)
1.10 pat 352: if (addsubunit(doingtop ^ flip
353: ? theunit->numerator
354: : theunit->denominator, item)) {
355: free(savescr);
1.1 deraadt 356: return 1;
1.10 pat 357: }
1.1 deraadt 358: }
359: item = strtok(NULL, " *\t/\n");
360: }
361: doingtop--;
362: if (slash) {
363: scratch = slash + 1;
1.9 deraadt 364: } else
1.1 deraadt 365: doingtop--;
366: } while (doingtop >= 0);
367: free(savescr);
368: return 0;
369: }
370:
371:
1.9 deraadt 372: int
1.1 deraadt 373: compare(const void *item1, const void *item2)
374: {
375: return strcmp(*(char **) item1, *(char **) item2);
376: }
377:
378:
1.9 deraadt 379: void
380: sortunit(struct unittype *theunit)
1.1 deraadt 381: {
382: char **ptr;
383: int count;
384:
385: for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
386: qsort(theunit->numerator, count, sizeof(char *), compare);
387: for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
388: qsort(theunit->denominator, count, sizeof(char *), compare);
389: }
390:
391:
1.9 deraadt 392: void
393: cancelunit(struct unittype *theunit)
1.1 deraadt 394: {
395: char **den, **num;
396: int comp;
397:
398: den = theunit->denominator;
399: num = theunit->numerator;
400:
401: while (*num && *den) {
402: comp = strcmp(*den, *num);
403: if (!comp) {
404: *den++ = NULLUNIT;
405: *num++ = NULLUNIT;
1.9 deraadt 406: } else if (comp < 0)
1.1 deraadt 407: den++;
408: else
409: num++;
410: }
411: }
412:
413:
414:
415:
416: /*
417: Looks up the definition for the specified unit.
418: Returns a pointer to the definition or a null pointer
419: if the specified unit does not appear in the units table.
420: */
421:
1.15 jmc 422: static char buffer[500]; /* buffer for lookupunit answers with
1.1 deraadt 423: prefixes */
424:
425: char *
426: lookupunit(char *unit)
427: {
1.13 ray 428: size_t len;
1.1 deraadt 429: int i;
430: char *copy;
431:
432: for (i = 0; i < unitcount; i++) {
433: if (!strcmp(unittable[i].uname, unit))
434: return unittable[i].uval;
435: }
436:
1.13 ray 437: len = strlen(unit);
438: if (len == 0)
439: return NULL;
440: if (unit[len - 1] == '^') {
1.1 deraadt 441: copy = dupstr(unit);
1.13 ray 442: copy[len - 1] = '\0';
1.1 deraadt 443: for (i = 0; i < unitcount; i++) {
444: if (!strcmp(unittable[i].uname, copy)) {
1.7 deraadt 445: strlcpy(buffer, copy, sizeof(buffer));
1.1 deraadt 446: free(copy);
447: return buffer;
448: }
449: }
450: free(copy);
451: }
1.13 ray 452: if (unit[len - 1] == 's') {
1.1 deraadt 453: copy = dupstr(unit);
1.13 ray 454: copy[len - 1] = '\0';
455: --len;
1.1 deraadt 456: for (i = 0; i < unitcount; i++) {
457: if (!strcmp(unittable[i].uname, copy)) {
1.7 deraadt 458: strlcpy(buffer, copy, sizeof(buffer));
1.1 deraadt 459: free(copy);
460: return buffer;
461: }
462: }
1.13 ray 463: if (len != 0 && copy[len - 1] == 'e') {
464: copy[len - 1] = 0;
1.1 deraadt 465: for (i = 0; i < unitcount; i++) {
466: if (!strcmp(unittable[i].uname, copy)) {
1.7 deraadt 467: strlcpy(buffer, copy, sizeof(buffer));
1.1 deraadt 468: free(copy);
469: return buffer;
470: }
471: }
472: }
473: free(copy);
474: }
475: for (i = 0; i < prefixcount; i++) {
1.13 ray 476: len = strlen(prefixtable[i].prefixname);
477: if (!strncmp(prefixtable[i].prefixname, unit, len)) {
1.15 jmc 478: if (!strlen(unit + len) || lookupunit(unit + len)) {
479: snprintf(buffer, sizeof(buffer), "%s %s",
480: prefixtable[i].prefixval, unit + len);
1.1 deraadt 481: return buffer;
482: }
483: }
484: }
1.13 ray 485: return NULL;
1.1 deraadt 486: }
487:
488:
489:
490: /*
491: reduces a product of symbolic units to primitive units.
492: The three low bits are used to return flags:
493:
494: bit 0 (1) set on if reductions were performed without error.
495: bit 1 (2) set on if no reductions are performed.
496: bit 2 (4) set on if an unknown unit is discovered.
497: */
498:
499:
500: #define ERROR 4
501:
1.9 deraadt 502: int
503: reduceproduct(struct unittype *theunit, int flip)
1.1 deraadt 504: {
1.9 deraadt 505: char *toadd, **product;
1.1 deraadt 506: int didsomething = 2;
507:
508: if (flip)
509: product = theunit->denominator;
510: else
511: product = theunit->numerator;
512:
513: for (; *product; product++) {
514:
515: for (;;) {
516: if (!strlen(*product))
517: break;
518: toadd = lookupunit(*product);
519: if (!toadd) {
520: printf("unknown unit '%s'\n", *product);
521: return ERROR;
522: }
523: if (strchr(toadd, PRIMITIVECHAR))
524: break;
525: didsomething = 1;
526: if (*product != NULLUNIT) {
527: free(*product);
528: *product = NULLUNIT;
529: }
530: if (addunit(theunit, toadd, flip))
531: return ERROR;
532: }
533: }
534: return didsomething;
535: }
536:
537:
538: /*
539: Reduces numerator and denominator of the specified unit.
540: Returns 0 on success, or 1 on unknown unit error.
541: */
542:
1.9 deraadt 543: int
544: reduceunit(struct unittype *theunit)
1.1 deraadt 545: {
546: int ret;
547:
548: ret = 1;
549: while (ret & 1) {
550: ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
551: if (ret & 4)
552: return 1;
553: }
554: return 0;
555: }
556:
557:
1.9 deraadt 558: int
1.1 deraadt 559: compareproducts(char **one, char **two)
560: {
561: while (*one || *two) {
562: if (!*one && *two != NULLUNIT)
563: return 1;
564: if (!*two && *one != NULLUNIT)
565: return 1;
566: if (*one == NULLUNIT)
567: one++;
568: else if (*two == NULLUNIT)
569: two++;
570: else if (strcmp(*one, *two))
571: return 1;
572: else
573: one++, two++;
574: }
575: return 0;
576: }
577:
578:
579: /* Return zero if units are compatible, nonzero otherwise */
580:
1.9 deraadt 581: int
582: compareunits(struct unittype *first, struct unittype *second)
1.1 deraadt 583: {
1.9 deraadt 584: return compareproducts(first->numerator, second->numerator) ||
585: compareproducts(first->denominator, second->denominator);
1.1 deraadt 586: }
587:
588:
1.9 deraadt 589: int
590: completereduce(struct unittype *unit)
1.1 deraadt 591: {
592: if (reduceunit(unit))
593: return 1;
594: sortunit(unit);
595: cancelunit(unit);
596: return 0;
597: }
598:
599:
1.9 deraadt 600: void
601: showanswer(struct unittype *have, struct unittype *want)
1.1 deraadt 602: {
603: if (compareunits(have, want)) {
604: printf("conformability error\n");
605: showunit(have);
606: showunit(want);
1.9 deraadt 607: } else
1.1 deraadt 608: printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
609: want->factor / have->factor);
610: }
611:
612:
1.9 deraadt 613: void
1.8 deraadt 614: usage(void)
1.1 deraadt 615: {
1.14 jmc 616: fprintf(stderr,
1.18 jmc 617: "usage: units [-qv] [-f filename] [[count] from-unit to-unit]\n");
1.1 deraadt 618: exit(3);
619: }
620:
621:
1.2 deraadt 622: int
1.1 deraadt 623: main(int argc, char **argv)
624: {
625:
626: struct unittype have, want;
627: char havestr[81], wantstr[81];
1.2 deraadt 628: int optchar;
1.1 deraadt 629: char *userfile = 0;
630: int quiet = 0;
631:
632: extern char *optarg;
633: extern int optind;
634:
1.21 ! deraadt 635: if (tame("stdio rpath", NULL) == -1)
! 636: err(1, "tame");
! 637:
1.2 deraadt 638: while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
1.1 deraadt 639: switch (optchar) {
640: case 'f':
641: userfile = optarg;
642: break;
643: case 'q':
644: quiet = 1;
645: break;
646: case 'v':
1.4 deraadt 647: fprintf(stderr,
648: "units version %s Copyright (c) 1993 by Adrian Mariano\n",
1.1 deraadt 649: VERSION);
1.4 deraadt 650: fprintf(stderr,
651: "This program may be freely distributed\n");
1.1 deraadt 652: usage();
653: default:
654: usage();
655: break;
656: }
657: }
658:
1.18 jmc 659: argc -= optind;
660: argv += optind;
661:
662: if (argc != 3 && argc != 2 && argc != 0)
1.1 deraadt 663: usage();
664:
665: readunits(userfile);
1.21 ! deraadt 666:
! 667: if (tame("stdio", NULL) == -1)
! 668: err(1, "tame");
1.1 deraadt 669:
1.18 jmc 670: if (argc == 3) {
671: strlcpy(havestr, argv[0], sizeof(havestr));
672: strlcat(havestr, " ", sizeof(havestr));
673: strlcat(havestr, argv[1], sizeof(havestr));
674: argc--;
675: argv++;
676: argv[0] = havestr;
677: }
678:
679: if (argc == 2) {
680: strlcpy(havestr, argv[0], sizeof(havestr));
681: strlcpy(wantstr, argv[1], sizeof(wantstr));
1.1 deraadt 682: initializeunit(&have);
683: addunit(&have, havestr, 0);
684: completereduce(&have);
685: initializeunit(&want);
686: addunit(&want, wantstr, 0);
687: completereduce(&want);
688: showanswer(&have, &want);
1.9 deraadt 689: } else {
1.1 deraadt 690: if (!quiet)
1.4 deraadt 691: printf("%d units, %d prefixes\n", unitcount,
1.1 deraadt 692: prefixcount);
693: for (;;) {
694: do {
695: initializeunit(&have);
696: if (!quiet)
697: printf("You have: ");
1.12 ray 698: if (!fgets(havestr, sizeof(havestr), stdin)) {
1.4 deraadt 699: if (!quiet)
700: putchar('\n');
1.1 deraadt 701: exit(0);
702: }
703: } while (addunit(&have, havestr, 0) ||
704: completereduce(&have));
705: do {
706: initializeunit(&want);
707: if (!quiet)
708: printf("You want: ");
1.12 ray 709: if (!fgets(wantstr, sizeof(wantstr), stdin)) {
1.1 deraadt 710: if (!quiet)
711: putchar('\n');
712: exit(0);
713: }
714: } while (addunit(&want, wantstr, 0) ||
715: completereduce(&want));
716: showanswer(&have, &want);
717: }
718: }
1.4 deraadt 719: return (0);
1.1 deraadt 720: }