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