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