Annotation of src/usr.bin/units/units.c, Revision 1.2
1.2 ! deraadt 1: /* $NetBSD: units.c,v 1.6 1996/04/06 06:01:03 thorpej Exp $ */
! 2:
1.1 deraadt 3: /*
4: * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. The name of the author may not be used to endorse or promote products
12: * derived from this software without specific prior written permission.
13: * Disclaimer: This software is provided by the author "as is". The author
14: * shall not be liable for any damages caused in any way by this software.
15: *
16: * I would appreciate (though I do not require) receiving a copy of any
17: * improvements you might make to this program.
18: */
19:
20: #include <ctype.h>
21: #include <stdio.h>
22: #include <string.h>
23: #include <stdlib.h>
24:
25: #include "pathnames.h"
26:
27: #define VERSION "1.0"
28:
29: #ifndef UNITSFILE
30: #define UNITSFILE _PATH_UNITSLIB
31: #endif
32:
33: #define MAXUNITS 1000
34: #define MAXPREFIXES 50
35:
36: #define MAXSUBUNITS 500
37:
38: #define PRIMITIVECHAR '!'
39:
40: char *powerstring = "^";
41:
42: struct {
43: char *uname;
44: char *uval;
45: } unittable[MAXUNITS];
46:
47: struct unittype {
48: char *numerator[MAXSUBUNITS];
49: char *denominator[MAXSUBUNITS];
50: double factor;
51: };
52:
53: struct {
54: char *prefixname;
55: char *prefixval;
56: } prefixtable[MAXPREFIXES];
57:
58:
59: char *NULLUNIT = "";
60:
61: int unitcount;
62: int prefixcount;
63:
64:
65: char *
66: dupstr(char *str)
67: {
68: char *ret;
69:
70: ret = malloc(strlen(str) + 1);
71: if (!ret) {
72: fprintf(stderr, "Memory allocation error\n");
73: exit(3);
74: }
75: strcpy(ret, str);
76: return (ret);
77: }
78:
79:
80: void
81: readerror(int linenum)
82: {
83: fprintf(stderr, "Error in units file '%s' line %d\n", UNITSFILE,
84: linenum);
85: }
86:
87:
88: void
89: readunits(char *userfile)
90: {
91: FILE *unitfile;
92: char line[80], *lineptr;
93: int len, linenum, i;
94:
95: unitcount = 0;
96: linenum = 0;
97:
98: if (userfile) {
99: unitfile = fopen(userfile, "rt");
100: if (!unitfile) {
101: fprintf(stderr, "Unable to open units file '%s'\n",
102: userfile);
103: exit(1);
104: }
105: }
106: else {
107: unitfile = fopen(UNITSFILE, "rt");
108: if (!unitfile) {
109: char *direc, *env;
110: char filename[1000];
111: char separator[2];
112:
113: env = getenv("PATH");
114: if (env) {
115: if (strchr(env, ';'))
116: strcpy(separator, ";");
117: else
118: strcpy(separator, ":");
119: direc = strtok(env, separator);
120: while (direc) {
121: strcpy(filename, "");
122: strncat(filename, direc, 999);
123: strncat(filename, "/",
124: 999 - strlen(filename));
125: strncat(filename, UNITSFILE,
126: 999 - strlen(filename));
127: unitfile = fopen(filename, "rt");
128: if (unitfile)
129: break;
130: direc = strtok(NULL, separator);
131: }
132: }
133: if (!unitfile) {
134: fprintf(stderr, "Can't find units file '%s'\n",
135: UNITSFILE);
136: exit(1);
137: }
138: }
139: }
140: while (!feof(unitfile)) {
141: if (!fgets(line, 79, unitfile))
142: break;
143: linenum++;
144: lineptr = line;
145: if (*lineptr == '/')
146: continue;
147: lineptr += strspn(lineptr, " \n\t");
148: len = strcspn(lineptr, " \n\t");
149: lineptr[len] = 0;
150: if (!strlen(lineptr))
151: continue;
152: if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
153: if (prefixcount == MAXPREFIXES) {
154: fprintf(stderr, "Memory for prefixes exceeded in line %d\n",
155: linenum);
156: continue;
157: }
158: lineptr[strlen(lineptr) - 1] = 0;
159: prefixtable[prefixcount].prefixname = dupstr(lineptr);
160: for (i = 0; i < prefixcount; i++)
161: if (!strcmp(prefixtable[i].prefixname, lineptr)) {
162: fprintf(stderr, "Redefinition of prefix '%s' on line %d ignored\n",
163: lineptr, linenum);
164: continue;
165: }
166: lineptr += len + 1;
167: if (!strlen(lineptr)) {
168: readerror(linenum);
169: continue;
170: }
171: lineptr += strspn(lineptr, " \n\t");
172: len = strcspn(lineptr, "\n\t");
173: lineptr[len] = 0;
174: prefixtable[prefixcount++].prefixval = dupstr(lineptr);
175: }
176: else { /* it's not a prefix */
177: if (unitcount == MAXUNITS) {
178: fprintf(stderr, "Memory for units exceeded in line %d\n",
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)) {
185: fprintf(stderr, "Redefinition of unit '%s' on line %d ignored\n",
186: lineptr, linenum);
187: continue;
188: }
189: lineptr += len + 1;
190: lineptr += strspn(lineptr, " \n\t");
191: if (!strlen(lineptr)) {
192: readerror(linenum);
193: continue;
194: }
195: len = strcspn(lineptr, "\n\t");
196: lineptr[len] = 0;
197: unittable[unitcount++].uval = dupstr(lineptr);
198: }
199: }
200: fclose(unitfile);
201: }
202:
203: void
204: initializeunit(struct unittype * theunit)
205: {
206: theunit->factor = 1.0;
207: theunit->numerator[0] = theunit->denominator[0] = NULL;
208: }
209:
210:
211: int
212: addsubunit(char *product[], char *toadd)
213: {
214: char **ptr;
215:
216: for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
217: if (ptr >= product + MAXSUBUNITS) {
218: fprintf(stderr, "Memory overflow in unit reduction\n");
219: return 1;
220: }
221: if (!*ptr)
222: *(ptr + 1) = 0;
223: *ptr = dupstr(toadd);
224: return 0;
225: }
226:
227:
228: void
229: showunit(struct unittype * theunit)
230: {
231: char **ptr;
232: int printedslash;
233: int counter = 1;
234:
235: printf("\t%.8g", theunit->factor);
236: for (ptr = theunit->numerator; *ptr; ptr++) {
237: if (ptr > theunit->numerator && **ptr &&
238: !strcmp(*ptr, *(ptr - 1)))
239: counter++;
240: else {
241: if (counter > 1)
242: printf("%s%d", powerstring, counter);
243: if (**ptr)
244: printf(" %s", *ptr);
245: counter = 1;
246: }
247: }
248: if (counter > 1)
249: printf("%s%d", powerstring, counter);
250: counter = 1;
251: printedslash = 0;
252: for (ptr = theunit->denominator; *ptr; ptr++) {
253: if (ptr > theunit->denominator && **ptr &&
254: !strcmp(*ptr, *(ptr - 1)))
255: counter++;
256: else {
257: if (counter > 1)
258: printf("%s%d", powerstring, counter);
259: if (**ptr) {
260: if (!printedslash)
261: printf(" /");
262: printedslash = 1;
263: printf(" %s", *ptr);
264: }
265: counter = 1;
266: }
267: }
268: if (counter > 1)
269: printf("%s%d", powerstring, counter);
270: printf("\n");
271: }
272:
273:
274: void
275: zeroerror()
276: {
277: fprintf(stderr, "Unit reduces to zero\n");
278: }
279:
280: /*
281: Adds the specified string to the unit.
282: Flip is 0 for adding normally, 1 for adding reciprocal.
283:
284: Returns 0 for successful addition, nonzero on error.
285: */
286:
287: int
288: addunit(struct unittype * theunit, char *toadd, int flip)
289: {
290: char *scratch, *savescr;
291: char *item;
292: char *divider, *slash;
293: int doingtop;
294:
295: savescr = scratch = dupstr(toadd);
296: for (slash = scratch + 1; *slash; slash++)
297: if (*slash == '-' &&
298: (tolower(*(slash - 1)) != 'e' ||
299: !strchr(".0123456789", *(slash + 1))))
300: *slash = ' ';
301: slash = strchr(scratch, '/');
302: if (slash)
303: *slash = 0;
304: doingtop = 1;
305: do {
306: item = strtok(scratch, " *\t\n/");
307: while (item) {
308: if (strchr("0123456789.", *item)) { /* item is a number */
309: double num;
310:
311: divider = strchr(item, '|');
312: if (divider) {
313: *divider = 0;
314: num = atof(item);
315: if (!num) {
316: zeroerror();
317: return 1;
318: }
319: if (doingtop ^ flip)
320: theunit->factor *= num;
321: else
322: theunit->factor /= num;
323: num = atof(divider + 1);
324: if (!num) {
325: zeroerror();
326: return 1;
327: }
328: if (doingtop ^ flip)
329: theunit->factor /= num;
330: else
331: theunit->factor *= num;
332: }
333: else {
334: num = atof(item);
335: if (!num) {
336: zeroerror();
337: return 1;
338: }
339: if (doingtop ^ flip)
340: theunit->factor *= num;
341: else
342: theunit->factor /= num;
343:
344: }
345: }
346: else { /* item is not a number */
347: int repeat = 1;
348:
349: if (strchr("23456789",
350: item[strlen(item) - 1])) {
351: repeat = item[strlen(item) - 1] - '0';
352: item[strlen(item) - 1] = 0;
353: }
354: for (; repeat; repeat--)
355: if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
356: return 1;
357: }
358: item = strtok(NULL, " *\t/\n");
359: }
360: doingtop--;
361: if (slash) {
362: scratch = slash + 1;
363: }
364: else
365: doingtop--;
366: } while (doingtop >= 0);
367: free(savescr);
368: return 0;
369: }
370:
371:
372: int
373: compare(const void *item1, const void *item2)
374: {
375: return strcmp(*(char **) item1, *(char **) item2);
376: }
377:
378:
379: void
380: sortunit(struct unittype * theunit)
381: {
382: char **ptr;
383: int count;
384:
385: for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
386: qsort(theunit->numerator, count, sizeof(char *), compare);
387: for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
388: qsort(theunit->denominator, count, sizeof(char *), compare);
389: }
390:
391:
392: void
393: cancelunit(struct unittype * theunit)
394: {
395: char **den, **num;
396: int comp;
397:
398: den = theunit->denominator;
399: num = theunit->numerator;
400:
401: while (*num && *den) {
402: comp = strcmp(*den, *num);
403: if (!comp) {
404: /* if (*den!=NULLUNIT) free(*den);
405: if (*num!=NULLUNIT) free(*num);*/
406: *den++ = NULLUNIT;
407: *num++ = NULLUNIT;
408: }
409: else if (comp < 0)
410: den++;
411: else
412: num++;
413: }
414: }
415:
416:
417:
418:
419: /*
420: Looks up the definition for the specified unit.
421: Returns a pointer to the definition or a null pointer
422: if the specified unit does not appear in the units table.
423: */
424:
425: static char buffer[100]; /* buffer for lookupunit answers with
426: prefixes */
427:
428: char *
429: lookupunit(char *unit)
430: {
431: int i;
432: char *copy;
433:
434: for (i = 0; i < unitcount; i++) {
435: if (!strcmp(unittable[i].uname, unit))
436: return unittable[i].uval;
437: }
438:
439: if (unit[strlen(unit) - 1] == '^') {
440: copy = dupstr(unit);
441: copy[strlen(copy) - 1] = 0;
442: for (i = 0; i < unitcount; i++) {
443: if (!strcmp(unittable[i].uname, copy)) {
444: strcpy(buffer, copy);
445: free(copy);
446: return buffer;
447: }
448: }
449: free(copy);
450: }
451: if (unit[strlen(unit) - 1] == 's') {
452: copy = dupstr(unit);
453: copy[strlen(copy) - 1] = 0;
454: for (i = 0; i < unitcount; i++) {
455: if (!strcmp(unittable[i].uname, copy)) {
456: strcpy(buffer, copy);
457: free(copy);
458: return buffer;
459: }
460: }
461: if (copy[strlen(copy) - 1] == 'e') {
462: copy[strlen(copy) - 1] = 0;
463: for (i = 0; i < unitcount; i++) {
464: if (!strcmp(unittable[i].uname, copy)) {
465: strcpy(buffer, copy);
466: free(copy);
467: return buffer;
468: }
469: }
470: }
471: free(copy);
472: }
473: for (i = 0; i < prefixcount; i++) {
474: if (!strncmp(prefixtable[i].prefixname, unit,
475: strlen(prefixtable[i].prefixname))) {
476: unit += strlen(prefixtable[i].prefixname);
477: if (!strlen(unit) || lookupunit(unit)) {
478: strcpy(buffer, prefixtable[i].prefixval);
479: strcat(buffer, " ");
480: strcat(buffer, unit);
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: {
620: fprintf(stderr, "\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
621: fprintf(stderr, "\n -f specify units file\n");
622: fprintf(stderr, " -q supress prompting (quiet)\n");
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':
650: fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
651: VERSION);
652: fprintf(stderr, " This program may be freely distributed\n");
653: usage();
654: default:
655: usage();
656: break;
657: }
658: }
659:
660: if (optind != argc - 2 && optind != argc)
661: usage();
662:
663: readunits(userfile);
664:
665: if (optind == argc - 2) {
666: strcpy(havestr, argv[optind]);
667: strcpy(wantstr, argv[optind + 1]);
668: initializeunit(&have);
669: addunit(&have, havestr, 0);
670: completereduce(&have);
671: initializeunit(&want);
672: addunit(&want, wantstr, 0);
673: completereduce(&want);
674: showanswer(&have, &want);
675: }
676: else {
677: if (!quiet)
678: printf("%d units, %d prefixes\n\n", unitcount,
679: prefixcount);
680: for (;;) {
681: do {
682: initializeunit(&have);
683: if (!quiet)
684: printf("You have: ");
685: if (!fgets(havestr, 80, stdin)) {
686: if (!quiet);
687: putchar('\n');
688: exit(0);
689: }
690: } while (addunit(&have, havestr, 0) ||
691: completereduce(&have));
692: do {
693: initializeunit(&want);
694: if (!quiet)
695: printf("You want: ");
696: if (!fgets(wantstr, 80, stdin)) {
697: if (!quiet)
698: putchar('\n');
699: exit(0);
700: }
701: } while (addunit(&want, wantstr, 0) ||
702: completereduce(&want));
703: showanswer(&have, &want);
704: }
705: }
706: }