Annotation of src/usr.bin/units/units.c, Revision 1.1
1.1 ! deraadt 1: /*
! 2: * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
! 3: *
! 4: * Redistribution and use in source and binary forms, with or without
! 5: * modification, are permitted provided that the following conditions
! 6: * are met:
! 7: * 1. Redistributions of source code must retain the above copyright
! 8: * notice, this list of conditions and the following disclaimer.
! 9: * 2. The name of the author may not be used to endorse or promote products
! 10: * derived from this software without specific prior written permission.
! 11: * Disclaimer: This software is provided by the author "as is". The author
! 12: * shall not be liable for any damages caused in any way by this software.
! 13: *
! 14: * I would appreciate (though I do not require) receiving a copy of any
! 15: * improvements you might make to this program.
! 16: *
! 17: * $Id: units.c,v 1.3 1994/12/21 07:22:00 jtc Exp $
! 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:
! 628: void
! 629: main(int argc, char **argv)
! 630: {
! 631:
! 632: struct unittype have, want;
! 633: char havestr[81], wantstr[81];
! 634: char optchar;
! 635: char *userfile = 0;
! 636: int quiet = 0;
! 637:
! 638: extern char *optarg;
! 639: extern int optind;
! 640:
! 641: while (EOF != (optchar = getopt(argc, argv, "vqf:"))) {
! 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: }