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