examples/Stdlib/ConsoleApp/UNITS.C

00001 // UNITS.C
00002 //
00003 /*
00004 Copyright (c) 1997-2010 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
00005 
00006 Redistribution and use in source and binary forms, with or without
00007 modification, are permitted provided that the following conditions are met:
00008 
00009 * Redistributions of source code must retain the above copyright notice, this
00010   list of conditions and the following disclaimer.
00011 * Redistributions in binary form must reproduce the above copyright notice,
00012   this list of conditions and the following disclaimer in the documentation
00013   and/or other materials provided with the distribution.
00014 * Neither the name of Nokia Corporation nor the names of its contributors
00015   may be used to endorse or promote products derived from this software
00016   without specific prior written permission.
00017 
00018 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00019 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00021 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
00022 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00023 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
00024 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00025 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00027 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00028 
00029 Description:  
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]; // was [MAXSUBUNITS]
00061         char *denominator[225]; // was [MAXSUBUNITS]
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] == '-') { /* it's a prefix */
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 {          /* it's not a prefix */
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    Adds the specified string to the unit.
00294    Flip is 0 for adding normally, 1 for adding reciprocal.
00295 
00296    Returns 0 for successful addition, nonzero on error.
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)) { /* item is a number */
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 {  /* item is not a number */
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 /*      if (*den!=NULLUNIT) free(*den);
00417       if (*num!=NULLUNIT) free(*num);*/
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    Looks up the definition for the specified unit.
00433    Returns a pointer to the definition or a null pointer
00434    if the specified unit does not appear in the units table.
00435 */
00436 
00437 static char buffer[100];        /* buffer for lookupunit answers with
00438                                    prefixes */
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    reduces a product of symbolic units to primitive units.
00504    The three low bits are used to return flags:
00505 
00506      bit 0 (1) set on if reductions were performed without error.
00507      bit 1 (2) set on if no reductions are performed.
00508      bit 2 (4) set on if an unknown unit is discovered.
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    Reduces numerator and denominator of the specified unit.
00554    Returns 0 on success, or 1 on unknown unit error.
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 /* Return zero if units are compatible, nonzero otherwise */
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                                                                                                         

Generated by  doxygen 1.6.2