examples/Stdlib/ConsoleApp/UNITS.C

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                                                                                                         

Generated by  doxygen 1.6.2