00001 // UNITS.C 00002 // 00003 // Copyright (c) 1997-2009 Nokia Corporation and/or its subsidiary(-ies). 00004 // All rights reserved. 00005 // This component and the accompanying materials are made available 00006 // under the terms of "Eclipse Public License v1.0" 00007 // which accompanies this distribution, and is available 00008 // at the URL "http://www.eclipse.org/legal/epl-v10.html". 00009 // 00010 // Initial Contributors: 00011 // Nokia Corporation - initial contribution. 00012 // 00013 // Contributors: 00014 // 00015 // Description: 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]; // was [MAXSUBUNITS] 00046 char *denominator[225]; // was [MAXSUBUNITS] 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] == '-') { /* it's a prefix */ 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 { /* it's not a prefix */ 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 Adds the specified string to the unit. 00279 Flip is 0 for adding normally, 1 for adding reciprocal. 00280 00281 Returns 0 for successful addition, nonzero on error. 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)) { /* item is a number */ 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 { /* item is not a number */ 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 /* if (*den!=NULLUNIT) free(*den); 00402 if (*num!=NULLUNIT) free(*num);*/ 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 Looks up the definition for the specified unit. 00418 Returns a pointer to the definition or a null pointer 00419 if the specified unit does not appear in the units table. 00420 */ 00421 00422 static char buffer[100]; /* buffer for lookupunit answers with 00423 prefixes */ 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 reduces a product of symbolic units to primitive units. 00489 The three low bits are used to return flags: 00490 00491 bit 0 (1) set on if reductions were performed without error. 00492 bit 1 (2) set on if no reductions are performed. 00493 bit 2 (4) set on if an unknown unit is discovered. 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 Reduces numerator and denominator of the specified unit. 00539 Returns 0 on success, or 1 on unknown unit error. 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 /* Return zero if units are compatible, nonzero otherwise */ 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
Copyright ©2010 Nokia Corporation and/or its subsidiary(-ies).
All rights
reserved. Unless otherwise stated, these materials are provided under the terms of the Eclipse Public License
v1.0.