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