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