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