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