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