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