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