home *** CD-ROM | disk | FTP | other *** search
- /*
- units.c Copyright (c) 1993 by Adrian Mariano
-
- This program can be freely distributed as long as no money is
- charged for it.
- */
-
-
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- #include <ctype.h>
-
- #if defined(__SASC_650) && defined(_AMIGA)
- #include <dos.h>
- char __stdiowin[] = "CON:0/0/640/200/Units";
- #endif
-
- #define VERSION "$VER: units 1.0 (14.7.93)"
-
- #ifndef UNITSFILE
- #define UNITSFILE "units.lib"
- #endif
-
- #define MAXUNITS 1000
- #define MAXPREFIXES 50
-
- #define MAXSUBUNITS 500
-
- #define PRIMITIVECHAR '!'
-
- /* prototypes as desired by SAS/C 6.51 for Amiga (Ron Charlton) */
- #include "units_protos.h"
- #include "getopt_protos.h"
-
- char *powerstring="^";
-
- struct {
- char *uname;
- char *uval;
- } unittable[MAXUNITS];
-
- struct unittype {
- char *numerator[MAXSUBUNITS];
- char *denominator[MAXSUBUNITS];
- double factor;
- };
-
- struct {
- char *prefixname;
- char *prefixval;
- } prefixtable[MAXPREFIXES];
-
-
- char *NULLUNIT="";
-
- int unitcount;
- int prefixcount;
-
-
- char *dupstr(char *str)
- {
- char *ret;
- ret=malloc(strlen(str)+1);
- if (!ret) {
- fprintf(stderr,"Memory allocation error\n");
- exit(3);
- }
- strcpy(ret,str);
- return(ret);
- }
-
-
- void readerror(int linenum)
- {
- fprintf(stderr,"Error in units file '%s' line %d\n",UNITSFILE,linenum);
- }
-
-
- void readunits(char *userfile)
- {
- FILE *unitfile;
- char line[80],*lineptr;
- int len,linenum,i;
-
- unitcount=0;
- linenum=0;
-
- if (userfile){
- unitfile=fopen(userfile,"rt");
- if (!unitfile) {
- fprintf(stderr,"Unable to open units file '%s'\n",userfile);
- exit(1);
- }
- } else {
- unitfile=fopen(UNITSFILE,"rt");
- if (!unitfile) {
- char *direc,*env;
- char filename[1000];
- char separator[2];
- env=getenv("PATH");
- if (env){
- if (strchr(env,';')) strcpy(separator,";");
- else strcpy(separator,":");
- direc=strtok(env,separator);
- while(direc){
- strcpy(filename,"");
- strncat(filename,direc,999);
- strncat(filename,"/",999-strlen(filename));
- strncat(filename,UNITSFILE,999-strlen(filename));
- unitfile=fopen(filename,"rt");
- if (unitfile) break;
- direc=strtok(NULL,separator);
- }
- }
- if (!unitfile){
- fprintf(stderr,"Can't find units file '%s'\n",UNITSFILE);
- exit(1);
- }
- }
- }
- while (!feof(unitfile)){
- if (!fgets(line,79,unitfile)) break;
- linenum++;
- lineptr=line;
- if (*lineptr=='/') continue;
- lineptr+=strspn(lineptr," \n\t");
- len = strcspn(lineptr, " \n\t");
- lineptr[len]=0;
- if (!strlen(lineptr)) continue;
- if (lineptr[strlen(lineptr)-1]=='-'){ /* it's a prefix */
- if (prefixcount==MAXPREFIXES) {
- fprintf(stderr,"Memory for prefixes exceeded in line %d\n",linenum);
- continue;
- }
- lineptr[strlen(lineptr)-1]=0;
- prefixtable[prefixcount].prefixname=dupstr(lineptr);
- for(i=0;i<prefixcount;i++)
- if (!strcmp(prefixtable[i].prefixname,lineptr)){
- fprintf(stderr,"Redefinition of prefix '%s' on line %d ignored\n",
- lineptr,linenum);
- continue;
- }
- lineptr+=len+1;
- if (!strlen(lineptr)) { readerror(linenum); continue;}
- lineptr+=strspn(lineptr," \n\t");
- len = strcspn(lineptr, "\n\t");
- lineptr[len]=0;
- prefixtable[prefixcount++].prefixval=dupstr(lineptr);
- } else { /* it's not a prefix */
- if (unitcount==MAXUNITS) {
- fprintf(stderr,"Memory for units exceeded in line %d\n",linenum);
- continue;
- }
- unittable[unitcount].uname=dupstr(lineptr);
- for(i=0;i<unitcount;i++) if (!strcmp(unittable[i].uname,lineptr)){
- fprintf(stderr,"Redefinition of unit '%s' on line %d ignored\n",
- lineptr,linenum);
- continue;
- }
- lineptr+=len+1;
- lineptr+=strspn(lineptr," \n\t");
- if (!strlen(lineptr)) { readerror(linenum); continue;}
- len = strcspn(lineptr, "\n\t");
- lineptr[len]=0;
- unittable[unitcount++].uval=dupstr(lineptr);
- }
- }
- fclose(unitfile);
- }
-
- void initializeunit(struct unittype *theunit)
- {
- theunit->factor=1.0;
- theunit->numerator[0]=theunit->denominator[0]=NULL;
- }
-
-
- int addsubunit(char *product[], char *toadd)
- {
- char **ptr;
-
- for(ptr=product;*ptr && *ptr!=NULLUNIT;ptr++);
- if (ptr>=product+MAXSUBUNITS){
- fprintf(stderr,"Memory overflow in unit reduction\n");
- return 1;
- }
- if (!*ptr) *(ptr+1)=0;
- *ptr=dupstr(toadd);
- return 0;
- }
-
-
- void showunit(struct unittype *theunit)
- {
- char **ptr;
- int printedslash;
- int counter=1;
-
- printf("\t%.8g",theunit->factor);
- for(ptr=theunit->numerator;*ptr;ptr++){
- if (ptr>theunit->numerator && **ptr &&
- !strcmp(*ptr,*(ptr-1))) counter++;
- else {
- if (counter>1) printf("%s%d",powerstring,counter);
- if (**ptr) printf(" %s",*ptr);
- counter=1;
- }
- }
- if (counter>1) printf("%s%d",powerstring,counter);
- counter=1;
- printedslash=0;
- for(ptr=theunit->denominator;*ptr;ptr++){
- if (ptr>theunit->denominator && **ptr &&
- !strcmp(*ptr,*(ptr-1))) counter++;
- else {
- if (counter>1) printf("%s%d",powerstring,counter);
- if (**ptr) {
- if (!printedslash) printf(" /");
- printedslash=1;
- printf(" %s",*ptr);
- }
- counter=1;
- }
- }
- if (counter>1) printf("%s%d",powerstring,counter);
- printf("\n");
- }
-
-
- void zeroerror()
- {
- fprintf(stderr,"Unit reduces to zero\n");
- }
-
- /*
- Adds the specified string to the unit.
- Flip is 0 for adding normally, 1 for adding reciprocal.
-
- Returns 0 for successful addition, nonzero on error.
- */
-
- int addunit(struct unittype *theunit,char *toadd,int flip)
- {
- char *scratch,*savescr;
- char *item;
- char *divider,*slash;
- int doingtop;
-
- savescr=scratch=dupstr(toadd);
- for(slash=scratch+1;*slash;slash++)
- if (*slash=='-' &&
- (tolower(*(slash-1))!='e' || !strchr(".0123456789",*(slash+1))))
- *slash=' ';
- slash=strchr(scratch,'/');
- if (slash) *slash=0;
- doingtop=1;
- do{
- item=strtok(scratch," *\t\n/");
- while(item){
- if (strchr("0123456789.",*item)){ /* item is a number */
- double num;
-
- divider=strchr(item,'|');
- if (divider){
- *divider=0;
- num=atof(item);
- if (!num) {
- zeroerror();
- return 1;
- }
- if (doingtop^flip) theunit->factor *= num;
- else theunit->factor /=num;
- num=atof(divider+1);
- if (!num) {
- zeroerror();
- return 1;
- }
- if (doingtop^flip) theunit->factor /= num;
- else theunit->factor *= num;
- } else {
- num=atof(item);
- if (!num) {
- zeroerror();
- return 1;
- }
- if (doingtop^flip) theunit->factor *= num;
- else theunit->factor /= num;
-
- }
- } else { /* item is not a number */
- int repeat=1;
- if (strchr("23456789",item[strlen(item)-1])){
- repeat=item[strlen(item)-1]-'0';
- item[strlen(item)-1]=0;
- }
- for(;repeat;repeat--)
- if (addsubunit(doingtop^flip?theunit->numerator:theunit->denominator,item))
- return 1;
- }
- item=strtok(NULL," *\t/\n");
- }
- doingtop--;
- if (slash){
- scratch=slash+1;
- } else doingtop--;
- }while (doingtop>=0 );
- free(savescr);
- return 0;
- }
-
-
- int compare(const void *item1, const void *item2)
- {
- return strcmp(*(char **) item1, * (char **) item2);
- }
-
-
- void sortunit(struct unittype *theunit)
- {
- char **ptr;
- int count;
-
- for(count=0,ptr=theunit->numerator;*ptr;ptr++,count++);
- qsort(theunit->numerator, count, sizeof(char *), compare);
- for(count=0,ptr=theunit->denominator;*ptr;ptr++,count++);
- qsort(theunit->denominator, count, sizeof(char *), compare);
- }
-
-
- void cancelunit(struct unittype *theunit)
- {
- char **den,**num;
- int comp;
-
- den=theunit->denominator;
- num=theunit->numerator;
-
- while(*num && *den){
- comp=strcmp(*den,*num);
- if (!comp) {
- /* if (*den!=NULLUNIT) free(*den);
- if (*num!=NULLUNIT) free(*num);*/
- *den++=NULLUNIT;
- *num++=NULLUNIT;
- }
- else if (comp<0) den++;
- else num++;
- }
- }
-
-
-
-
- /*
- Looks up the definition for the specified unit.
- Returns a pointer to the definition or a null pointer
- if the specified unit does not appear in the units table.
- */
-
- static char buffer[100]; /* buffer for lookupunit answers with prefixes */
-
- char *lookupunit(char *unit)
- {
- int i;
- char *copy;
-
- for(i=0;i<unitcount;i++){
- if (!strcmp(unittable[i].uname,unit)) return unittable[i].uval;
- }
-
- if (unit[strlen(unit)-1]=='^') {
- copy=dupstr(unit);
- copy[strlen(copy)-1]=0;
- for(i=0;i<unitcount;i++){
- if (!strcmp(unittable[i].uname,copy)) {
- strcpy(buffer,copy);
- free(copy);
- return buffer;
- }
- }
- free(copy);
- }
- if (unit[strlen(unit)-1]=='s') {
- copy=dupstr(unit);
- copy[strlen(copy)-1]=0;
- for(i=0;i<unitcount;i++){
- if (!strcmp(unittable[i].uname,copy)) {
- strcpy(buffer,copy);
- free(copy);
- return buffer;
- }
- }
- if (copy[strlen(copy)-1]=='e') {
- copy[strlen(copy)-1]=0;
- for(i=0;i<unitcount;i++){
- if (!strcmp(unittable[i].uname,copy)){
- strcpy(buffer,copy);
- free(copy);
- return buffer;
- }
- }
- }
- free(copy);
- }
- for(i=0;i<prefixcount;i++){
- if (!strncmp(prefixtable[i].prefixname,unit,
- strlen(prefixtable[i].prefixname))) {
- unit += strlen(prefixtable[i].prefixname);
- if (!strlen(unit) || lookupunit(unit)){
- strcpy(buffer,prefixtable[i].prefixval);
- strcat(buffer," ");
- strcat(buffer,unit);
- return buffer;
- }
- }
- }
- return 0;
- }
-
-
-
- /*
- reduces a product of symbolic units to primitive units.
- The three low bits are used to return flags:
-
- bit 0 (1) set on if reductions were performed without error.
- bit 1 (2) set on if no reductions are performed.
- bit 2 (4) set on if an unknown unit is discovered.
- */
-
-
- #define ERROR 4
-
- int reduceproduct(struct unittype *theunit, int flip)
- {
-
- char *toadd;
- char **product;
- int didsomething=2;
-
- if (flip) product=theunit->denominator;
- else product=theunit->numerator;
-
- for(;*product;product++){
-
- for(;;){
- if (!strlen(*product)) break;
- toadd=lookupunit(*product);
- if (!toadd) {
- printf("unknown unit '%s'\n",*product);
- return ERROR;
- }
- if (strchr(toadd,PRIMITIVECHAR)) break;
- didsomething=1;
- if (*product!=NULLUNIT) {
- free (*product);
- *product=NULLUNIT;
- }
- if (addunit(theunit, toadd, flip)) return ERROR;
- }
- }
- return didsomething;
- }
-
-
- /*
- Reduces numerator and denominator of the specified unit.
- Returns 0 on success, or 1 on unknown unit error.
- */
-
- int reduceunit(struct unittype *theunit)
- {
- int ret;
- ret=1;
- while(ret & 1){
- ret = reduceproduct(theunit,0) | reduceproduct(theunit,1);
- if (ret & 4) return 1;
- }
- return 0;
- }
-
-
- int compareproducts(char **one,char **two)
- {
- while(*one || *two){
- if (!*one && *two!=NULLUNIT) return 1;
- if (!*two && *one!=NULLUNIT) return 1;
- if (*one==NULLUNIT) one++;
- else if (*two==NULLUNIT) two++;
- else if (strcmp(*one,*two)) return 1;
- else one++,two++;
- }
- return 0;
- }
-
-
- /* Return zero if units are compatible, nonzero otherwise */
-
- int compareunits(struct unittype *first, struct unittype *second)
- {
- return
- compareproducts(first->numerator,second->numerator) ||
- compareproducts(first->denominator,second->denominator);
- }
-
-
- int completereduce(struct unittype *unit)
- {
- if (reduceunit(unit)) return 1;
- sortunit(unit);
- cancelunit(unit);
- return 0;
- }
-
-
- void showanswer(struct unittype *have, struct unittype *want)
- {
- if (compareunits(have,want)) {
- printf("conformability error\n");
- showunit(have);
- showunit(want);
- }
- else printf("\t* %.8g\n\t/ %.8g\n",have->factor/want->factor,
- want->factor/have->factor);
- }
-
-
- void usage()
- {
- fprintf(stderr,"\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
- fprintf(stderr,"\n -f specify units file\n");
- fprintf(stderr," -q supress prompting (quiet)\n");
- fprintf(stderr," -v print version number\n");
- exit(3);
- }
-
-
- void main(int argc, char **argv){
-
- struct unittype have,want;
- char havestr[81],wantstr[81];
- char optchar;
- char *userfile=0;
- int quiet=0;
-
- extern char *optarg;
- extern int optind;
-
- #if defined(__SASC_650) && defined(_AMIGA)
- if(argc == 0) {
- /* Invoked from WorkBench... use WorkBench arguments
- * Make sure <dos.h> was included earlier in order to
- * declare _WBArgc and _WBArgv.
- */
- argc = _WBArgc;
- argv = _WBArgv;
- }
- #endif
-
- while (EOF != (optchar = getopt(argc,argv,"vqf:"))) {
- switch(optchar) {
- case 'f':
- userfile=optarg;
- break;
- case 'q':
- quiet=1;
- break;
- case 'v':
- fprintf(stderr,"\n %s Copyright (c) 1993 by Adrian Mariano\n",VERSION+6);
- fprintf(stderr," This program may be freely distributed\n");
- usage();
- default:
- usage();
- break;
- }
- }
-
- if (optind != argc-2 && optind!=argc) usage();
-
- readunits(userfile);
-
- if (optind==argc-2){
- strcpy(havestr,argv[optind]);
- strcpy(wantstr,argv[optind+1]);
- initializeunit(&have);
- addunit(&have,havestr,0);
- completereduce(&have);
- initializeunit(&want);
- addunit(&want,wantstr,0);
- completereduce(&want);
- showanswer(&have,&want);
- } else {
- if (!quiet) printf("%d units, %d prefixes\n\n",unitcount,prefixcount);
- for(;;){
- do{
- initializeunit(&have);
- if (!quiet) printf("You have: ");
- if (!fgets(havestr,80,stdin)) { if (!quiet);putchar('\n');exit(0);}
- if (!strnicmp(havestr, "quit", 4)){ if (!quiet);putchar('\n');exit(0);}
- } while(addunit(&have,havestr,0) || completereduce(&have));
- do{
- initializeunit(&want);
- if (!quiet) printf("You want: ");
- if (!fgets(wantstr,80,stdin)) {if (!quiet) putchar('\n');exit(0);}
- if (!strnicmp(wantstr, "quit", 4)){if (!quiet);putchar('\n');exit(0);}
- } while(addunit(&want,wantstr,0) || completereduce(&want));
- showanswer(&have,&want);
- }
- }
- }
-
-
-
-
-