00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include <ctype.h>
00019 #include <stdio.h>
00020 #include <string.h>
00021 #include <stdlib.h>
00022
00023 #include "PATHNAME.H"
00024
00025 #define VERSION "1.0"
00026
00027 #ifndef UNITSFILE
00028 #define UNITSFILE _PATH_UNITSLIB
00029 #endif
00030
00031 #define MAXUNITS 1000
00032 #define MAXPREFIXES 50
00033 #define MAXSUBUNITS 500
00034
00035 #define PRIMITIVECHAR '!'
00036
00037 char *powerstring = "^";
00038
00039 struct {
00040 char *uname;
00041 char *uval;
00042 } unittable[MAXUNITS];
00043
00044 struct unittype {
00045 char *numerator[225];
00046 char *denominator[225];
00047 double factor;
00048 };
00049
00050 struct {
00051 char *prefixname;
00052 char *prefixval;
00053 } prefixtable[MAXPREFIXES];
00054
00055
00056 char *NULLUNIT = "";
00057
00058 int unitcount;
00059 int prefixcount;
00060
00061
00062 char *
00063 dupstr(char *str)
00064 {
00065 char *ret;
00066
00067 ret = (char *)malloc(strlen(str) + 1);
00068 if (!ret) {
00069 fprintf(stderr, "Memory allocation error\n");
00070 exit(3);
00071 }
00072 strcpy(ret, str);
00073 return (ret);
00074 }
00075
00076
00077 void
00078 readerror(int linenum)
00079 {
00080 fprintf(stderr, "Error in units file '%s' line %d\n", UNITSFILE,
00081 linenum);
00082 }
00083
00084
00085 void
00086 readunits(char *userfile)
00087 {
00088 FILE *unitfile;
00089 char line[80], *lineptr;
00090 int len, linenum, i;
00091
00092 unitcount = 0;
00093 linenum = 0;
00094
00095 if (userfile) {
00096 unitfile = fopen(userfile, "rt");
00097 if (!unitfile) {
00098 fprintf(stderr, "Unable to open units file '%s'\n",
00099 userfile);
00100 exit(1);
00101 }
00102 }
00103 else {
00104 unitfile = fopen(UNITSFILE, "rt");
00105 if (!unitfile) {
00106 char *direc, *env;
00107 char filename[1000];
00108 char separator[2];
00109
00110 env = getenv("PATH");
00111 if (env) {
00112 if (strchr(env, ';'))
00113 strcpy(separator, ";");
00114 else
00115 strcpy(separator, ":");
00116 direc = strtok(env, separator);
00117 while (direc) {
00118 strcpy(filename, "");
00119 strncat(filename, direc, 999);
00120 strncat(filename, "/",
00121 999 - strlen(filename));
00122 strncat(filename, UNITSFILE,
00123 999 - strlen(filename));
00124 unitfile = fopen(filename, "rt");
00125 if (unitfile)
00126 break;
00127 direc = strtok(NULL, separator);
00128 }
00129 }
00130 if (!unitfile) {
00131 fprintf(stderr, "Can't find units file '%s'\n",
00132 UNITSFILE);
00133 exit(1);
00134 }
00135 }
00136 }
00137 while (!feof(unitfile)) {
00138 if (!fgets(line, 79, unitfile))
00139 break;
00140 linenum++;
00141 lineptr = line;
00142 if (*lineptr == '/')
00143 continue;
00144 lineptr += strspn(lineptr, " \n\t");
00145 len = strcspn(lineptr, " \n\t");
00146 lineptr[len] = 0;
00147 if (!strlen(lineptr))
00148 continue;
00149 if (lineptr[strlen(lineptr) - 1] == '-') {
00150 if (prefixcount == MAXPREFIXES) {
00151 fprintf(stderr, "Memory for prefixes exceeded in line %d\n",
00152 linenum);
00153 continue;
00154 }
00155 lineptr[strlen(lineptr) - 1] = 0;
00156 prefixtable[prefixcount].prefixname = dupstr(lineptr);
00157 for (i = 0; i < prefixcount; i++)
00158 if (!strcmp(prefixtable[i].prefixname, lineptr)) {
00159 fprintf(stderr, "Redefinition of prefix '%s' on line %d ignored\n",
00160 lineptr, linenum);
00161 continue;
00162 }
00163 lineptr += len + 1;
00164 if (!strlen(lineptr)) {
00165 readerror(linenum);
00166 continue;
00167 }
00168 lineptr += strspn(lineptr, " \n\t");
00169 len = strcspn(lineptr, "\n\t");
00170 lineptr[len] = 0;
00171 prefixtable[prefixcount++].prefixval = dupstr(lineptr);
00172 }
00173 else {
00174 if (unitcount == MAXUNITS) {
00175 fprintf(stderr, "Memory for units exceeded in line %d\n",
00176 linenum);
00177 continue;
00178 }
00179 unittable[unitcount].uname = dupstr(lineptr);
00180 for (i = 0; i < unitcount; i++)
00181 if (!strcmp(unittable[i].uname, lineptr)) {
00182 fprintf(stderr, "Redefinition of unit '%s' on line %d ignored\n",
00183 lineptr, linenum);
00184 continue;
00185 }
00186 lineptr += len + 1;
00187 lineptr += strspn(lineptr, " \n\t");
00188 if (!strlen(lineptr)) {
00189 readerror(linenum);
00190 continue;
00191 }
00192 len = strcspn(lineptr, "\n\t");
00193 lineptr[len] = 0;
00194 unittable[unitcount++].uval = dupstr(lineptr);
00195 }
00196 }
00197 fclose(unitfile);
00198 }
00199
00200 void
00201 initializeunit(struct unittype * theunit)
00202 {
00203 theunit->factor = 1.0;
00204 theunit->numerator[0] = theunit->denominator[0] = NULL;
00205 }
00206
00207
00208 int
00209 addsubunit(char *product[], char *toadd)
00210 {
00211 char **ptr;
00212
00213 for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++){};
00214 if (ptr >= product + MAXSUBUNITS) {
00215 fprintf(stderr, "Memory overflow in unit reduction\n");
00216 return 1;
00217 }
00218 if (!*ptr)
00219 *(ptr + 1) = 0;
00220 *ptr = dupstr(toadd);
00221 return 0;
00222 }
00223
00224
00225 void
00226 showunit(struct unittype * theunit)
00227 {
00228 char **ptr;
00229 int printedslash;
00230 int counter = 1;
00231
00232 printf("\t%.8g", theunit->factor);
00233 for (ptr = theunit->numerator; *ptr; ptr++) {
00234 if (ptr > theunit->numerator && **ptr &&
00235 !strcmp(*ptr, *(ptr - 1)))
00236 counter++;
00237 else {
00238 if (counter > 1)
00239 printf("%s%d", powerstring, counter);
00240 if (**ptr)
00241 printf(" %s", *ptr);
00242 counter = 1;
00243 }
00244 }
00245 if (counter > 1)
00246 printf("%s%d", powerstring, counter);
00247 counter = 1;
00248 printedslash = 0;
00249 for (ptr = theunit->denominator; *ptr; ptr++) {
00250 if (ptr > theunit->denominator && **ptr &&
00251 !strcmp(*ptr, *(ptr - 1)))
00252 counter++;
00253 else {
00254 if (counter > 1)
00255 printf("%s%d", powerstring, counter);
00256 if (**ptr) {
00257 if (!printedslash)
00258 printf(" /");
00259 printedslash = 1;
00260 printf(" %s", *ptr);
00261 }
00262 counter = 1;
00263 }
00264 }
00265 if (counter > 1)
00266 printf("%s%d", powerstring, counter);
00267 printf("\n");
00268 }
00269
00270
00271 void
00272 zeroerror()
00273 {
00274 fprintf(stderr, "Unit reduces to zero\n");
00275 }
00276
00277
00278
00279
00280
00281
00282
00283
00284 int
00285 addunit(struct unittype * theunit, char *toadd, int flip)
00286 {
00287 char *scratch, *savescr;
00288 char *item;
00289 char *divider, *slash;
00290 int doingtop;
00291
00292 savescr = scratch = dupstr(toadd);
00293 for (slash = scratch + 1; *slash; slash++)
00294 if (*slash == '-' &&
00295 (tolower(*(slash - 1)) != 'e' ||
00296 !strchr(".0123456789", *(slash + 1))))
00297 *slash = ' ';
00298 slash = strchr(scratch, '/');
00299 if (slash)
00300 *slash = 0;
00301 doingtop = 1;
00302 do {
00303 item = strtok(scratch, " *\t\n/");
00304 while (item) {
00305 if (strchr("0123456789.", *item)) {
00306 double num;
00307
00308 divider = strchr(item, '|');
00309 if (divider) {
00310 *divider = 0;
00311 num = atof(item);
00312 if (!num) {
00313 zeroerror();
00314 return 1;
00315 }
00316 if (doingtop ^ flip)
00317 theunit->factor *= num;
00318 else
00319 theunit->factor /= num;
00320 num = atof(divider + 1);
00321 if (!num) {
00322 zeroerror();
00323 return 1;
00324 }
00325 if (doingtop ^ flip)
00326 theunit->factor /= num;
00327 else
00328 theunit->factor *= num;
00329 }
00330 else {
00331 num = atof(item);
00332 if (!num) {
00333 zeroerror();
00334 return 1;
00335 }
00336 if (doingtop ^ flip)
00337 theunit->factor *= num;
00338 else
00339 theunit->factor /= num;
00340
00341 }
00342 }
00343 else {
00344 int repeat = 1;
00345
00346 if (strchr("23456789",
00347 item[strlen(item) - 1])) {
00348 repeat = item[strlen(item) - 1] - '0';
00349 item[strlen(item) - 1] = 0;
00350 }
00351 for (; repeat; repeat--)
00352 if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
00353 return 1;
00354 }
00355 item = strtok(NULL, " *\t/\n");
00356 }
00357 doingtop--;
00358 if (slash) {
00359 scratch = slash + 1;
00360 }
00361 else
00362 doingtop--;
00363 } while (doingtop >= 0);
00364 free(savescr);
00365 return 0;
00366 }
00367
00368
00369 int
00370 compare(const void *item1, const void *item2)
00371 {
00372 return strcmp(*(char **) item1, *(char **) item2);
00373 }
00374
00375
00376 void
00377 sortunit(struct unittype * theunit)
00378 {
00379 char **ptr;
00380 int count;
00381
00382 for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++){};
00383 qsort(theunit->numerator, count, sizeof(char *), compare);
00384 for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++){};
00385 qsort(theunit->denominator, count, sizeof(char *), compare);
00386 }
00387
00388
00389 void
00390 cancelunit(struct unittype * theunit)
00391 {
00392 char **den, **num;
00393 int comp;
00394
00395 den = theunit->denominator;
00396 num = theunit->numerator;
00397
00398 while (*num && *den) {
00399 comp = strcmp(*den, *num);
00400 if (!comp) {
00401
00402
00403 *den++ = NULLUNIT;
00404 *num++ = NULLUNIT;
00405 }
00406 else if (comp < 0)
00407 den++;
00408 else
00409 num++;
00410 }
00411 }
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 static char buffer[100];
00423
00424
00425 char *
00426 lookupunit(char *unit)
00427 {
00428 int i;
00429 char *copy;
00430
00431 for (i = 0; i < unitcount; i++) {
00432 if (!strcmp(unittable[i].uname, unit))
00433 return unittable[i].uval;
00434 }
00435
00436 if (unit[strlen(unit) - 1] == '^') {
00437 copy = dupstr(unit);
00438 copy[strlen(copy) - 1] = 0;
00439 for (i = 0; i < unitcount; i++) {
00440 if (!strcmp(unittable[i].uname, copy)) {
00441 strcpy(buffer, copy);
00442 free(copy);
00443 return buffer;
00444 }
00445 }
00446 free(copy);
00447 }
00448 if (unit[strlen(unit) - 1] == 's') {
00449 copy = dupstr(unit);
00450 copy[strlen(copy) - 1] = 0;
00451 for (i = 0; i < unitcount; i++) {
00452 if (!strcmp(unittable[i].uname, copy)) {
00453 strcpy(buffer, copy);
00454 free(copy);
00455 return buffer;
00456 }
00457 }
00458 if (copy[strlen(copy) - 1] == 'e') {
00459 copy[strlen(copy) - 1] = 0;
00460 for (i = 0; i < unitcount; i++) {
00461 if (!strcmp(unittable[i].uname, copy)) {
00462 strcpy(buffer, copy);
00463 free(copy);
00464 return buffer;
00465 }
00466 }
00467 }
00468 free(copy);
00469 }
00470 for (i = 0; i < prefixcount; i++) {
00471 if (!strncmp(prefixtable[i].prefixname, unit,
00472 strlen(prefixtable[i].prefixname))) {
00473 unit += strlen(prefixtable[i].prefixname);
00474 if (!strlen(unit) || lookupunit(unit)) {
00475 strcpy(buffer, prefixtable[i].prefixval);
00476 strcat(buffer, " ");
00477 strcat(buffer, unit);
00478 return buffer;
00479 }
00480 }
00481 }
00482 return 0;
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497 #define ERROR 4
00498
00499 int
00500 reduceproduct(struct unittype * theunit, int flip)
00501 {
00502
00503 char *toadd;
00504 char **product;
00505 int didsomething = 2;
00506
00507 if (flip)
00508 product = theunit->denominator;
00509 else
00510 product = theunit->numerator;
00511
00512 for (; *product; product++) {
00513
00514 for (;;) {
00515 if (!strlen(*product))
00516 break;
00517 toadd = lookupunit(*product);
00518 if (!toadd) {
00519 printf("unknown unit '%s'\n", *product);
00520 return ERROR;
00521 }
00522 if (strchr(toadd, PRIMITIVECHAR))
00523 break;
00524 didsomething = 1;
00525 if (*product != NULLUNIT) {
00526 free(*product);
00527 *product = NULLUNIT;
00528 }
00529 if (addunit(theunit, toadd, flip))
00530 return ERROR;
00531 }
00532 }
00533 return didsomething;
00534 }
00535
00536
00537
00538
00539
00540
00541
00542 int
00543 reduceunit(struct unittype * theunit)
00544 {
00545 int ret;
00546
00547 ret = 1;
00548 while (ret & 1) {
00549 ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
00550 if (ret & 4)
00551 return 1;
00552 }
00553 return 0;
00554 }
00555
00556
00557 int
00558 compareproducts(char **one, char **two)
00559 {
00560 while (*one || *two) {
00561 if (!*one && *two != NULLUNIT)
00562 return 1;
00563 if (!*two && *one != NULLUNIT)
00564 return 1;
00565 if (*one == NULLUNIT)
00566 one++;
00567 else if (*two == NULLUNIT)
00568 two++;
00569 else if (strcmp(*one, *two))
00570 return 1;
00571 else
00572 one++, two++;
00573 }
00574 return 0;
00575 }
00576
00577
00578
00579
00580 int
00581 compareunits(struct unittype * first, struct unittype * second)
00582 {
00583 return
00584 compareproducts(first->numerator, second->numerator) ||
00585 compareproducts(first->denominator, second->denominator);
00586 }
00587
00588
00589 int
00590 completereduce(struct unittype * unit)
00591 {
00592 if (reduceunit(unit))
00593 return 1;
00594 sortunit(unit);
00595 cancelunit(unit);
00596 return 0;
00597 }
00598
00599
00600 void
00601 showanswer(struct unittype * have, struct unittype * want)
00602 {
00603 if (compareunits(have, want)) {
00604 printf("conformability error\n");
00605 showunit(have);
00606 showunit(want);
00607 }
00608 else
00609 printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
00610 want->factor / have->factor);
00611 }
00612
00613
00614 void
00615 usage()
00616 {
00617 fprintf(stderr, "\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
00618 fprintf(stderr, "\n -f specify units file\n");
00619 fprintf(stderr, " -q supress prompting (quiet)\n");
00620 fprintf(stderr, " -v print version number\n");
00621 exit(3);
00622 }
00623
00624 extern int getopt(int, char **, const char *);
00625
00626 int
00627 main(int argc, char **argv)
00628 {
00629
00630 struct unittype have, want;
00631 char havestr[81], wantstr[81];
00632 int optchar;
00633 char *userfile = 0;
00634 int quiet = 0;
00635
00636 extern char *optarg;
00637 extern int optind;
00638
00639 while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
00640 switch (optchar) {
00641 case 'f':
00642 userfile = optarg;
00643 break;
00644 case 'q':
00645 quiet = 1;
00646 break;
00647 case 'v':
00648 fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
00649 VERSION);
00650 fprintf(stderr, " This program may be freely distributed\n");
00651 usage();
00652 default:
00653 usage();
00654 break;
00655 }
00656 }
00657
00658 if (optind != argc - 2 && optind != argc)
00659 usage();
00660
00661 readunits(userfile);
00662
00663 if (optind == argc - 2) {
00664 strcpy(havestr, argv[optind]);
00665 strcpy(wantstr, argv[optind + 1]);
00666 initializeunit(&have);
00667 addunit(&have, havestr, 0);
00668 completereduce(&have);
00669 initializeunit(&want);
00670 addunit(&want, wantstr, 0);
00671 completereduce(&want);
00672 showanswer(&have, &want);
00673 }
00674 else {
00675 if (!quiet)
00676 printf("%d units, %d prefixes\n\n", unitcount,
00677 prefixcount);
00678 for (;;) {
00679 do {
00680 initializeunit(&have);
00681 if (!quiet)
00682 printf("You have: ");
00683 if (!fgets(havestr, 80, stdin)) {
00684 if (!quiet)
00685 putchar('\n');
00686 exit(0);
00687 }
00688 } while (addunit(&have, havestr, 0) ||
00689 completereduce(&have));
00690 do {
00691 initializeunit(&want);
00692 if (!quiet)
00693 printf("You want: ");
00694 if (!fgets(wantstr, 80, stdin)) {
00695 if (!quiet)
00696 putchar('\n');
00697 exit(0);
00698 }
00699 } while (addunit(&want, wantstr, 0) ||
00700 completereduce(&want));
00701 showanswer(&have, &want);
00702 }
00703 }
00704
00705 return(0);
00706 }
00707