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