home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / calculat / units_am.zip / UNITS.C < prev    next >
C/C++ Source or Header  |  1993-07-17  |  14KB  |  593 lines

  1. /* 
  2.    units.c   Copyright (c) 1993 by Adrian Mariano
  3.  
  4.    This program can be freely distributed as long as no money is
  5.    charged for it. 
  6. */
  7.  
  8.  
  9. #include<stdio.h>
  10. #include<string.h>
  11. #include<stdlib.h>
  12.  
  13. #define VERSION "1.0"
  14.  
  15. #ifndef UNITSFILE
  16. #define UNITSFILE "units.lib"
  17. #endif
  18.  
  19. #define MAXUNITS 1000
  20. #define MAXPREFIXES 50
  21.  
  22. #define MAXSUBUNITS 500
  23.  
  24. #define PRIMITIVECHAR '!'
  25.  
  26. char *powerstring="^";
  27.  
  28. struct {
  29.    char *uname;
  30.    char *uval;
  31. } unittable[MAXUNITS];
  32.  
  33. struct unittype {
  34.    char *numerator[MAXSUBUNITS];
  35.    char *denominator[MAXSUBUNITS];
  36.    double factor;
  37. };
  38.  
  39. struct {
  40.    char *prefixname;
  41.    char *prefixval;
  42. } prefixtable[MAXPREFIXES];
  43.  
  44.  
  45. char *NULLUNIT="";
  46.  
  47. int unitcount;
  48. int prefixcount;
  49.  
  50.  
  51. char *dupstr(char *str)
  52. {
  53.   char *ret;
  54.   ret=malloc(strlen(str)+1);
  55.   if (!ret) {
  56.     fprintf(stderr,"Memory allocation error\n");
  57.     exit(3);
  58.   }
  59.   strcpy(ret,str);
  60.   return(ret);
  61.  
  62.  
  63. void readerror(int linenum)
  64. {
  65.   fprintf(stderr,"Error in units file '%s' line %d\n",UNITSFILE,linenum);
  66. }
  67.  
  68.  
  69. void readunits(char *userfile)
  70. {
  71.   FILE *unitfile;
  72.   char line[80],*lineptr;
  73.   int len,linenum,i;
  74.  
  75.   unitcount=0;
  76.   linenum=0;
  77.  
  78.   if (userfile){
  79.     unitfile=fopen(userfile,"rt");
  80.     if (!unitfile) {
  81.       fprintf(stderr,"Unable to open units file '%s'\n",userfile);
  82.       exit(1);
  83.     }
  84.   } else {
  85.     unitfile=fopen(UNITSFILE,"rt");
  86.     if (!unitfile) { 
  87.       char *direc,*env;
  88.       char filename[1000];
  89.       char separator[2];
  90.       env=getenv("PATH");
  91.       if (env){
  92.         if (strchr(env,';')) strcpy(separator,";");
  93.         else strcpy(separator,":");
  94.         direc=strtok(env,separator);
  95.         while(direc){
  96.           strcpy(filename,"");
  97.           strncat(filename,direc,999);
  98.           strncat(filename,"/",999-strlen(filename));
  99.           strncat(filename,UNITSFILE,999-strlen(filename));
  100.           unitfile=fopen(filename,"rt");
  101.           if (unitfile) break;
  102.           direc=strtok(NULL,separator);
  103.         }
  104.       }
  105.       if (!unitfile){
  106.         fprintf(stderr,"Can't find units file '%s'\n",UNITSFILE);
  107.         exit(1);
  108.       }
  109.     }
  110.   }
  111.   while (!feof(unitfile)){
  112.     if (!fgets(line,79,unitfile)) break;
  113.     linenum++;
  114.     lineptr=line;
  115.     if (*lineptr=='/') continue;
  116.     lineptr+=strspn(lineptr," \n\t");
  117.     len = strcspn(lineptr, " \n\t");
  118.     lineptr[len]=0;
  119.     if (!strlen(lineptr)) continue;
  120.     if (lineptr[strlen(lineptr)-1]=='-'){   /* it's a prefix */
  121.       if (prefixcount==MAXPREFIXES) {
  122.         fprintf(stderr,"Memory for prefixes exceeded in line %d\n",linenum);
  123.         continue;
  124.       }
  125.       lineptr[strlen(lineptr)-1]=0;
  126.       prefixtable[prefixcount].prefixname=dupstr(lineptr);
  127.       for(i=0;i<prefixcount;i++) 
  128.         if (!strcmp(prefixtable[i].prefixname,lineptr)){
  129.         fprintf(stderr,"Redefinition of prefix '%s' on line %d ignored\n",
  130.             lineptr,linenum);
  131.     continue;
  132.       }
  133.       lineptr+=len+1;
  134.       if (!strlen(lineptr)) { readerror(linenum); continue;}
  135.       lineptr+=strspn(lineptr," \n\t");
  136.       len = strcspn(lineptr, "\n\t");
  137.       lineptr[len]=0;
  138.       prefixtable[prefixcount++].prefixval=dupstr(lineptr);
  139.     } else {  /* it's not a prefix */
  140.       if (unitcount==MAXUNITS) {
  141.         fprintf(stderr,"Memory for units exceeded in line %d\n",linenum);
  142.         continue;
  143.       }
  144.       unittable[unitcount].uname=dupstr(lineptr);
  145.       for(i=0;i<unitcount;i++) if (!strcmp(unittable[i].uname,lineptr)){
  146.         fprintf(stderr,"Redefinition of unit '%s' on line %d ignored\n",
  147.             lineptr,linenum);
  148.     continue;
  149.       }
  150.       lineptr+=len+1;
  151.       lineptr+=strspn(lineptr," \n\t");
  152.       if (!strlen(lineptr)) { readerror(linenum); continue;}
  153.       len = strcspn(lineptr, "\n\t");
  154.       lineptr[len]=0;
  155.       unittable[unitcount++].uval=dupstr(lineptr);
  156.     }
  157.   }
  158.   fclose(unitfile);
  159. }
  160.  
  161. void initializeunit(struct unittype *theunit)
  162. {
  163.   theunit->factor=1.0;
  164.   theunit->numerator[0]=theunit->denominator[0]=NULL;
  165. }
  166.  
  167.  
  168. int addsubunit(char *product[], char *toadd)
  169. {
  170.   char **ptr;
  171.  
  172.   for(ptr=product;*ptr && *ptr!=NULLUNIT;ptr++);
  173.   if (ptr>=product+MAXSUBUNITS){
  174.     fprintf(stderr,"Memory overflow in unit reduction\n");
  175.     return 1;
  176.   }
  177.   if (!*ptr) *(ptr+1)=0;
  178.   *ptr=dupstr(toadd);
  179.   return 0;
  180. }
  181.  
  182.  
  183. void showunit(struct unittype *theunit)
  184. {
  185.   char **ptr;
  186.   int printedslash;
  187.   int counter=1;
  188.  
  189.   printf("\t%.8g",theunit->factor);
  190.   for(ptr=theunit->numerator;*ptr;ptr++){
  191.     if (ptr>theunit->numerator && **ptr &&
  192.        !strcmp(*ptr,*(ptr-1))) counter++;
  193.       else {
  194.         if (counter>1) printf("%s%d",powerstring,counter);
  195.         if (**ptr) printf(" %s",*ptr);
  196.     counter=1;
  197.       }
  198.   }
  199.   if (counter>1) printf("%s%d",powerstring,counter);
  200.   counter=1;
  201.   printedslash=0;
  202.   for(ptr=theunit->denominator;*ptr;ptr++){
  203.     if (ptr>theunit->denominator && **ptr && 
  204.          !strcmp(*ptr,*(ptr-1))) counter++;
  205.       else {
  206.         if (counter>1) printf("%s%d",powerstring,counter);
  207.         if (**ptr) { 
  208.       if (!printedslash) printf(" /");
  209.       printedslash=1;
  210.           printf(" %s",*ptr);
  211.     }
  212.     counter=1;
  213.       }
  214.   }
  215.   if (counter>1) printf("%s%d",powerstring,counter);
  216.   printf("\n");
  217. }
  218.  
  219.  
  220. void zeroerror()
  221. {
  222.    fprintf(stderr,"Unit reduces to zero\n");
  223. }   
  224.  
  225. /* 
  226.    Adds the specified string to the unit. 
  227.    Flip is 0 for adding normally, 1 for adding reciprocal.
  228.  
  229.    Returns 0 for successful addition, nonzero on error.
  230. */
  231.  
  232. int addunit(struct unittype *theunit,char *toadd,int flip)
  233. {
  234.   char *scratch,*savescr;
  235.   char *item;
  236.   char *divider,*subunit,*slash;
  237.   int doingtop;
  238.  
  239.   savescr=scratch=dupstr(toadd);
  240.   for(slash=scratch+1;*slash;slash++) 
  241.     if (*slash=='-' && 
  242.       (tolower(*(slash-1))!='e' || !strchr(".0123456789",*(slash+1))))
  243.       *slash=' ';
  244.   slash=strchr(scratch,'/');
  245.   if (slash) *slash=0;
  246.   doingtop=1;
  247.   do{
  248.   item=strtok(scratch," *\t\n/");
  249.   while(item){
  250.     if (strchr("0123456789.",*item)){  /* item is a number */
  251.       double num;
  252.  
  253.       divider=strchr(item,'|');
  254.       if (divider){
  255.     *divider=0;
  256.     num=atof(item);
  257.     if (!num) {
  258.           zeroerror();
  259.           return 1;
  260.         }
  261.     if (doingtop^flip) theunit->factor *= num;
  262.          else  theunit->factor /=num;
  263.     num=atof(divider+1);
  264.     if (!num)  {
  265.           zeroerror();
  266.           return 1;
  267.         }
  268.     if (doingtop^flip) theunit->factor /= num;
  269.      else theunit->factor *= num;
  270.       } else {
  271.     num=atof(item);
  272.     if (!num)  {
  273.           zeroerror();
  274.           return 1;
  275.         }
  276.     if (doingtop^flip) theunit->factor *= num;
  277.     else theunit->factor /= num;
  278.  
  279.       }
  280.     } else {                         /* item is not a number */
  281.       int repeat=1;
  282.       if (strchr("23456789",item[strlen(item)-1])){
  283.     repeat=item[strlen(item)-1]-'0';
  284.     item[strlen(item)-1]=0;
  285.       }
  286.       for(;repeat;repeat--)
  287.     if (addsubunit(doingtop^flip?theunit->numerator:theunit->denominator,item))
  288.   return 1;
  289.     }
  290.     item=strtok(NULL," *\t/\n");
  291.   }
  292.   doingtop--;
  293.   if (slash){
  294.     scratch=slash+1;
  295.   } else doingtop--;
  296.   }while (doingtop>=0 );
  297.   free(savescr);
  298.   return 0;
  299. }
  300.  
  301.  
  302. int compare(const void *item1, const void *item2)
  303. {
  304.   return strcmp(*(char **) item1, * (char **) item2);
  305. }
  306.  
  307.  
  308. void sortunit(struct unittype *theunit)
  309. {
  310.   char **ptr;
  311.   int count;
  312.  
  313.   for(count=0,ptr=theunit->numerator;*ptr;ptr++,count++);
  314.   qsort(theunit->numerator, count, sizeof(char *), compare);
  315.   for(count=0,ptr=theunit->denominator;*ptr;ptr++,count++);
  316.   qsort(theunit->denominator, count, sizeof(char *), compare);
  317. }
  318.  
  319.  
  320. void cancelunit(struct unittype *theunit)
  321. {
  322.   char **den,**num;
  323.   int comp;
  324.  
  325.   den=theunit->denominator;
  326.   num=theunit->numerator;
  327.  
  328.   while(*num && *den){
  329.     comp=strcmp(*den,*num);
  330.     if (!comp) {
  331. /*      if (*den!=NULLUNIT) free(*den);
  332.       if (*num!=NULLUNIT) free(*num);*/
  333.       *den++=NULLUNIT;
  334.       *num++=NULLUNIT;
  335.     }
  336.     else if (comp<0) den++;
  337.     else num++;
  338.   }
  339. }
  340.  
  341.  
  342.  
  343.  
  344. /*
  345.    Looks up the definition for the specified unit.  
  346.    Returns a pointer to the definition or a null pointer
  347.    if the specified unit does not appear in the units table.
  348. */
  349.  
  350. static char buffer[100];  /* buffer for lookupunit answers with prefixes */
  351.  
  352. char *lookupunit(char *unit)
  353. {
  354.   int i;
  355.   char *copy;
  356.  
  357.   for(i=0;i<unitcount;i++){
  358.     if (!strcmp(unittable[i].uname,unit)) return unittable[i].uval;
  359.   }
  360.  
  361.   if (unit[strlen(unit)-1]=='^') {
  362.     copy=dupstr(unit);
  363.     copy[strlen(copy)-1]=0;
  364.     for(i=0;i<unitcount;i++){
  365.       if (!strcmp(unittable[i].uname,copy)) {
  366.         strcpy(buffer,copy);
  367.         free(copy);
  368.         return buffer;
  369.       }
  370.     } 
  371.     free(copy);
  372.   }
  373.   if (unit[strlen(unit)-1]=='s') {
  374.     copy=dupstr(unit);
  375.     copy[strlen(copy)-1]=0;
  376.     for(i=0;i<unitcount;i++){
  377.       if (!strcmp(unittable[i].uname,copy)) {
  378.         strcpy(buffer,copy);
  379.         free(copy);
  380.         return buffer;
  381.       }
  382.     }
  383.     if (copy[strlen(copy)-1]=='e') {
  384.       copy[strlen(copy)-1]=0;
  385.       for(i=0;i<unitcount;i++){
  386.         if (!strcmp(unittable[i].uname,copy)){
  387.           strcpy(buffer,copy);
  388.           free(copy);
  389.           return buffer;
  390.         }
  391.       }
  392.     }
  393.     free(copy);
  394.   }
  395.   for(i=0;i<prefixcount;i++){
  396.     if (!strncmp(prefixtable[i].prefixname,unit,
  397.         strlen(prefixtable[i].prefixname))) {
  398.       unit += strlen(prefixtable[i].prefixname);
  399.       if (!strlen(unit) || lookupunit(unit)){
  400.         strcpy(buffer,prefixtable[i].prefixval);
  401.         strcat(buffer," ");
  402.         strcat(buffer,unit);
  403.         return buffer;
  404.       }
  405.     }
  406.   }
  407.   return 0;
  408. }
  409.  
  410.  
  411.  
  412. /*
  413.    reduces a product of symbolic units to primitive units. 
  414.    The three low bits are used to return flags:
  415.  
  416.      bit 0 (1) set on if reductions were performed without error.
  417.      bit 1 (2) set on if no reductions are performed.
  418.      bit 2 (4) set on if an unknown unit is discovered.
  419. */
  420.  
  421.  
  422. #define ERROR 4
  423.  
  424. int reduceproduct(struct unittype *theunit, int flip)
  425. {
  426.  
  427.   char *toadd;
  428.   char **product;
  429.   int didsomething=2;
  430.  
  431.   if (flip) product=theunit->denominator;
  432.   else product=theunit->numerator;
  433.  
  434.   for(;*product;product++){
  435.     
  436.     for(;;){
  437.       if (!strlen(*product)) break;
  438.       toadd=lookupunit(*product);
  439.       if (!toadd) { 
  440.         printf("unknown unit '%s'\n",*product);
  441.         return ERROR;
  442.       }
  443.       if (strchr(toadd,PRIMITIVECHAR)) break;
  444.       didsomething=1;
  445.       if (*product!=NULLUNIT) {
  446.         free (*product);
  447.         *product=NULLUNIT;
  448.       }
  449.       if (addunit(theunit, toadd, flip)) return ERROR;
  450.     }
  451.   }
  452.   return didsomething;
  453. }
  454.  
  455.  
  456. /* 
  457.    Reduces numerator and denominator of the specified unit.
  458.    Returns 0 on success, or 1 on unknown unit error.
  459. */
  460.  
  461. int reduceunit(struct unittype *theunit)
  462. {
  463.   int ret;
  464.   ret=1;
  465.   while(ret & 1){
  466.     ret = reduceproduct(theunit,0) | reduceproduct(theunit,1);
  467.     if (ret & 4) return 1;
  468.   }
  469.   return 0;
  470. }
  471.  
  472.  
  473. int compareproducts(char **one,char **two)
  474. {
  475.   while(*one || *two){
  476.     if (!*one && *two!=NULLUNIT) return 1;
  477.     if (!*two && *one!=NULLUNIT) return 1;
  478.     if (*one==NULLUNIT) one++;
  479.     else if (*two==NULLUNIT) two++;
  480.     else if (strcmp(*one,*two)) return 1;
  481.     else one++,two++;
  482.   }
  483.   return 0;
  484. }
  485.  
  486.  
  487. /* Return zero if units are compatible, nonzero otherwise */
  488.  
  489. int compareunits(struct unittype *first, struct unittype *second)
  490. {
  491.   return 
  492.     compareproducts(first->numerator,second->numerator) ||
  493.     compareproducts(first->denominator,second->denominator);
  494. }
  495.  
  496.  
  497. int completereduce(struct unittype *unit)
  498. {
  499.   if (reduceunit(unit)) return 1;
  500.   sortunit(unit);
  501.   cancelunit(unit);
  502.   return 0;
  503. }
  504.  
  505.  
  506. void showanswer(struct unittype *have, struct unittype *want)
  507. {
  508.   if (compareunits(have,want)) {
  509.     printf("conformability error\n");
  510.     showunit(have);
  511.     showunit(want);
  512.   }
  513.   else printf("\t* %.8g\n\t/ %.8g\n",have->factor/want->factor,
  514.                            want->factor/have->factor);
  515. }
  516.  
  517.  
  518. void usage()
  519. {
  520.   fprintf(stderr,"\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
  521.   fprintf(stderr,"\n    -f specify units file\n");
  522.   fprintf(stderr,"    -q supress prompting (quiet)\n");
  523.   fprintf(stderr,"    -v print version number\n");
  524.   exit(3);
  525. }
  526.  
  527.  
  528. void main(int argc, char **argv){
  529.  
  530.   struct unittype have,want;
  531.   char havestr[81],wantstr[81];
  532.   char optchar;
  533.   char *userfile=0;
  534.   int quiet=0;
  535.  
  536.   extern char *optarg;
  537.   extern int optind;
  538.  
  539.   while (EOF != (optchar = getopt(argc,argv,"vqf:"))) {
  540.     switch(optchar) {
  541.       case 'f':
  542.         userfile=optarg;
  543.         break;
  544.       case 'q':
  545.         quiet=1;
  546.         break;
  547.       case 'v':
  548.         fprintf(stderr,"\n  units version %s  Copyright (c) 1993 by Adrian Mariano\n",VERSION);
  549.         fprintf(stderr,"                    This program may be freely distributed\n");
  550.         usage();
  551.       default:
  552.         usage();
  553.         break;
  554.      }
  555.   }
  556.     
  557.   if (optind != argc-2 && optind!=argc) usage();
  558.  
  559.   readunits(userfile);
  560.  
  561.   if (optind==argc-2){
  562.     strcpy(havestr,argv[optind]);
  563.     strcpy(wantstr,argv[optind+1]);
  564.     initializeunit(&have);
  565.     addunit(&have,havestr,0);
  566.     completereduce(&have);
  567.     initializeunit(&want);
  568.     addunit(&want,wantstr,0);
  569.     completereduce(&want);
  570.     showanswer(&have,&want);
  571.   } else {
  572.     if (!quiet) printf("%d units, %d prefixes\n\n",unitcount,prefixcount);
  573.     for(;;){
  574.       do{
  575.         initializeunit(&have);
  576.         if (!quiet) printf("You have: ");
  577.         if (!fgets(havestr,80,stdin)) { if (!quiet);putchar('\n');exit(0);}
  578.       } while(addunit(&have,havestr,0) || completereduce(&have));
  579.       do{
  580.         initializeunit(&want);
  581.         if (!quiet) printf("You want: ");
  582.         if (!fgets(wantstr,80,stdin)) {if (!quiet) putchar('\n');exit(0);}
  583.       } while(addunit(&want,wantstr,0) || completereduce(&want));
  584.       showanswer(&have,&want);
  585.     }
  586.   }
  587. }
  588.  
  589.  
  590.  
  591.  
  592.