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