116186Salex/*
216186Salex * units.c   Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
316186Salex *
416186Salex * Redistribution and use in source and binary forms, with or without
516186Salex * modification, are permitted provided that the following conditions
616186Salex * are met:
716186Salex * 1. Redistributions of source code must retain the above copyright
816186Salex *    notice, this list of conditions and the following disclaimer.
916186Salex * 2. The name of the author may not be used to endorse or promote products
1016186Salex *    derived from this software without specific prior written permission.
1116186Salex * Disclaimer:  This software is provided by the author "as is".  The author
1216186Salex * shall not be liable for any damages caused in any way by this software.
1316186Salex *
1416186Salex * I would appreciate (though I do not require) receiving a copy of any
1516186Salex * improvements you might make to this program.
1616186Salex */
1716186Salex
1828502Scharnier#ifndef lint
1928502Scharnierstatic const char rcsid[] =
2050477Speter  "$FreeBSD$";
2128502Scharnier#endif /* not lint */
2228502Scharnier
2316186Salex#include <ctype.h>
2428502Scharnier#include <err.h>
2516186Salex#include <stdio.h>
2628502Scharnier#include <stdlib.h>
2716186Salex#include <string.h>
2828502Scharnier#include <unistd.h>
2916186Salex
3016186Salex#include "pathnames.h"
3116186Salex
3216186Salex#define VERSION "1.0"
3316186Salex
3416186Salex#ifndef UNITSFILE
3516186Salex#define UNITSFILE _PATH_UNITSLIB
3616186Salex#endif
3716186Salex
3816186Salex#define MAXUNITS 1000
3973229Sdwmalone#define MAXPREFIXES 100
4016186Salex
4116186Salex#define MAXSUBUNITS 500
4216186Salex
4316186Salex#define PRIMITIVECHAR '!'
4416186Salex
45227194Sedstatic const char *powerstring = "^";
4616186Salex
47227194Sedstatic struct {
4816186Salex	char *uname;
4916186Salex	char *uval;
5016186Salex}      unittable[MAXUNITS];
5116186Salex
5216186Salexstruct unittype {
5316186Salex	char *numerator[MAXSUBUNITS];
5416186Salex	char *denominator[MAXSUBUNITS];
5516186Salex	double factor;
56181786Sdwmalone	double offset;
57181786Sdwmalone	int quantity;
5816186Salex};
5916186Salex
60227194Sedstatic struct {
6116186Salex	char *prefixname;
6216186Salex	char *prefixval;
6316186Salex}      prefixtable[MAXPREFIXES];
6416186Salex
6516186Salex
66227194Sedstatic char NULLUNIT[] = "";
6716186Salex
6864053Skris#ifdef MSDOS
6973229Sdwmalone#define SEPARATOR      ";"
7064053Skris#else
7164053Skris#define SEPARATOR      ":"
7264053Skris#endif
7364053Skris
74227194Sedstatic int unitcount;
75227194Sedstatic int prefixcount;
7616186Salex
7778698Sdwmalonechar	*dupstr(const char *str);
7878698Sdwmalonevoid	 readunits(const char *userfile);
7978698Sdwmalonevoid	 initializeunit(struct unittype * theunit);
8078698Sdwmaloneint	 addsubunit(char *product[], char *toadd);
8178698Sdwmalonevoid	 showunit(struct unittype * theunit);
8278698Sdwmalonevoid	 zeroerror(void);
83181786Sdwmaloneint	 addunit(struct unittype *theunit, char *toadd, int flip, int quantity);
8478698Sdwmaloneint	 compare(const void *item1, const void *item2);
8578698Sdwmalonevoid	 sortunit(struct unittype * theunit);
8678698Sdwmalonevoid	 cancelunit(struct unittype * theunit);
8778698Sdwmalonechar	*lookupunit(const char *unit);
8878698Sdwmaloneint	 reduceproduct(struct unittype * theunit, int flip);
8978698Sdwmaloneint	 reduceunit(struct unittype * theunit);
9078698Sdwmaloneint	 compareproducts(char **one, char **two);
9178698Sdwmaloneint	 compareunits(struct unittype * first, struct unittype * second);
9278698Sdwmaloneint	 completereduce(struct unittype * unit);
9378698Sdwmalonevoid	 showanswer(struct unittype * have, struct unittype * want);
9478698Sdwmalonevoid	 usage(void);
9516186Salex
9616186Salexchar *
9773229Sdwmalonedupstr(const char *str)
9816186Salex{
9916186Salex	char *ret;
10016186Salex
10116186Salex	ret = malloc(strlen(str) + 1);
10228502Scharnier	if (!ret)
10328502Scharnier		errx(3, "memory allocation error");
10416186Salex	strcpy(ret, str);
10516186Salex	return (ret);
10616186Salex}
10716186Salex
10816186Salex
10916186Salexvoid
11073229Sdwmalonereadunits(const char *userfile)
11116186Salex{
11216186Salex	FILE *unitfile;
11373229Sdwmalone	char line[512], *lineptr;
11416186Salex	int len, linenum, i;
11516186Salex
11616186Salex	unitcount = 0;
11716186Salex	linenum = 0;
11816186Salex
11916186Salex	if (userfile) {
12016186Salex		unitfile = fopen(userfile, "rt");
12128502Scharnier		if (!unitfile)
12228502Scharnier			errx(1, "unable to open units file '%s'", userfile);
12316186Salex	}
12416186Salex	else {
12516186Salex		unitfile = fopen(UNITSFILE, "rt");
12616186Salex		if (!unitfile) {
12716186Salex			char *direc, *env;
12816186Salex			char filename[1000];
12916186Salex
13016186Salex			env = getenv("PATH");
13116186Salex			if (env) {
13264053Skris				direc = strtok(env, SEPARATOR);
13316186Salex				while (direc) {
13464053Skris					snprintf(filename, sizeof(filename),
13564053Skris					    "%s/%s", direc, UNITSFILE);
13616186Salex					unitfile = fopen(filename, "rt");
13716186Salex					if (unitfile)
13816186Salex						break;
13964053Skris					direc = strtok(NULL, SEPARATOR);
14016186Salex				}
14116186Salex			}
14228502Scharnier			if (!unitfile)
14328502Scharnier				errx(1, "can't find units file '%s'", UNITSFILE);
14416186Salex		}
14516186Salex	}
14616186Salex	while (!feof(unitfile)) {
14773229Sdwmalone		if (!fgets(line, sizeof(line), unitfile))
14816186Salex			break;
14916186Salex		linenum++;
15016186Salex		lineptr = line;
15116186Salex		if (*lineptr == '/')
15216186Salex			continue;
15316186Salex		lineptr += strspn(lineptr, " \n\t");
15416186Salex		len = strcspn(lineptr, " \n\t");
15516186Salex		lineptr[len] = 0;
15616186Salex		if (!strlen(lineptr))
15716186Salex			continue;
15816186Salex		if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
15916186Salex			if (prefixcount == MAXPREFIXES) {
16028502Scharnier				warnx("memory for prefixes exceeded in line %d", linenum);
16116186Salex				continue;
16216186Salex			}
16316186Salex			lineptr[strlen(lineptr) - 1] = 0;
16416186Salex			prefixtable[prefixcount].prefixname = dupstr(lineptr);
16516186Salex			for (i = 0; i < prefixcount; i++)
16616186Salex				if (!strcmp(prefixtable[i].prefixname, lineptr)) {
16728502Scharnier					warnx("redefinition of prefix '%s' on line %d ignored",
16816186Salex					    lineptr, linenum);
16916186Salex					continue;
17016186Salex				}
17116186Salex			lineptr += len + 1;
17273229Sdwmalone			lineptr += strspn(lineptr, " \n\t");
17373229Sdwmalone			len = strcspn(lineptr, "\n\t");
17473229Sdwmalone			if (len == 0) {
17573229Sdwmalone				warnx("unexpected end of prefix on line %d",
17673229Sdwmalone				    linenum);
17716186Salex				continue;
17816186Salex			}
17916186Salex			lineptr[len] = 0;
18016186Salex			prefixtable[prefixcount++].prefixval = dupstr(lineptr);
18116186Salex		}
18216186Salex		else {		/* it's not a prefix */
18316186Salex			if (unitcount == MAXUNITS) {
18428502Scharnier				warnx("memory for units exceeded in line %d", linenum);
18516186Salex				continue;
18616186Salex			}
18716186Salex			unittable[unitcount].uname = dupstr(lineptr);
18816186Salex			for (i = 0; i < unitcount; i++)
18916186Salex				if (!strcmp(unittable[i].uname, lineptr)) {
19028502Scharnier					warnx("redefinition of unit '%s' on line %d ignored",
19116186Salex					    lineptr, linenum);
19216186Salex					continue;
19316186Salex				}
19416186Salex			lineptr += len + 1;
19516186Salex			lineptr += strspn(lineptr, " \n\t");
19616186Salex			if (!strlen(lineptr)) {
19773229Sdwmalone				warnx("unexpected end of unit on line %d",
19873229Sdwmalone				    linenum);
19916186Salex				continue;
20016186Salex			}
20116186Salex			len = strcspn(lineptr, "\n\t");
20216186Salex			lineptr[len] = 0;
20316186Salex			unittable[unitcount++].uval = dupstr(lineptr);
20416186Salex		}
20516186Salex	}
20616186Salex	fclose(unitfile);
20716186Salex}
20816186Salex
20916186Salexvoid
21016186Salexinitializeunit(struct unittype * theunit)
21116186Salex{
212181786Sdwmalone	theunit->numerator[0] = theunit->denominator[0] = NULL;
21316186Salex	theunit->factor = 1.0;
214181786Sdwmalone	theunit->offset = 0.0;
215181786Sdwmalone	theunit->quantity = 0;
21616186Salex}
21716186Salex
21816186Salex
21916186Salexint
22016186Salexaddsubunit(char *product[], char *toadd)
22116186Salex{
22216186Salex	char **ptr;
22316186Salex
22416186Salex	for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
22516186Salex	if (ptr >= product + MAXSUBUNITS) {
22628502Scharnier		warnx("memory overflow in unit reduction");
22716186Salex		return 1;
22816186Salex	}
22916186Salex	if (!*ptr)
23016186Salex		*(ptr + 1) = 0;
23116186Salex	*ptr = dupstr(toadd);
23216186Salex	return 0;
23316186Salex}
23416186Salex
23516186Salex
23616186Salexvoid
23716186Salexshowunit(struct unittype * theunit)
23816186Salex{
23916186Salex	char **ptr;
24016186Salex	int printedslash;
24116186Salex	int counter = 1;
24216186Salex
24316186Salex	printf("\t%.8g", theunit->factor);
244181786Sdwmalone	if (theunit->offset)
245181786Sdwmalone		printf("&%.8g", theunit->offset);
24616186Salex	for (ptr = theunit->numerator; *ptr; ptr++) {
24716186Salex		if (ptr > theunit->numerator && **ptr &&
24816186Salex		    !strcmp(*ptr, *(ptr - 1)))
24916186Salex			counter++;
25016186Salex		else {
25116186Salex			if (counter > 1)
25216186Salex				printf("%s%d", powerstring, counter);
25316186Salex			if (**ptr)
25416186Salex				printf(" %s", *ptr);
25516186Salex			counter = 1;
25616186Salex		}
25716186Salex	}
25816186Salex	if (counter > 1)
25916186Salex		printf("%s%d", powerstring, counter);
26016186Salex	counter = 1;
26116186Salex	printedslash = 0;
26216186Salex	for (ptr = theunit->denominator; *ptr; ptr++) {
26316186Salex		if (ptr > theunit->denominator && **ptr &&
26416186Salex		    !strcmp(*ptr, *(ptr - 1)))
26516186Salex			counter++;
26616186Salex		else {
26716186Salex			if (counter > 1)
26816186Salex				printf("%s%d", powerstring, counter);
26916186Salex			if (**ptr) {
27016186Salex				if (!printedslash)
27116186Salex					printf(" /");
27216186Salex				printedslash = 1;
27316186Salex				printf(" %s", *ptr);
27416186Salex			}
27516186Salex			counter = 1;
27616186Salex		}
27716186Salex	}
27816186Salex	if (counter > 1)
27916186Salex		printf("%s%d", powerstring, counter);
28016186Salex	printf("\n");
28116186Salex}
28216186Salex
28316186Salex
28416186Salexvoid
28573229Sdwmalonezeroerror(void)
28616186Salex{
28728502Scharnier	warnx("unit reduces to zero");
28816186Salex}
28916186Salex
29016186Salex/*
29116186Salex   Adds the specified string to the unit.
29216186Salex   Flip is 0 for adding normally, 1 for adding reciprocal.
293181786Sdwmalone   Quantity is 1 if this is a quantity to be converted rather than a pure unit.
29416186Salex
29516186Salex   Returns 0 for successful addition, nonzero on error.
29616186Salex*/
29716186Salex
29816186Salexint
299181786Sdwmaloneaddunit(struct unittype * theunit, char *toadd, int flip, int quantity)
30016186Salex{
30116186Salex	char *scratch, *savescr;
30216186Salex	char *item;
303181786Sdwmalone	char *divider, *slash, *offset;
30416186Salex	int doingtop;
30516186Salex
30664053Skris	if (!strlen(toadd))
30764053Skris		return 1;
30864053Skris
30916186Salex	savescr = scratch = dupstr(toadd);
31016186Salex	for (slash = scratch + 1; *slash; slash++)
31116186Salex		if (*slash == '-' &&
31216186Salex		    (tolower(*(slash - 1)) != 'e' ||
31316186Salex		    !strchr(".0123456789", *(slash + 1))))
31416186Salex			*slash = ' ';
31516186Salex	slash = strchr(scratch, '/');
31616186Salex	if (slash)
31716186Salex		*slash = 0;
31816186Salex	doingtop = 1;
31916186Salex	do {
32016186Salex		item = strtok(scratch, " *\t\n/");
32116186Salex		while (item) {
32216186Salex			if (strchr("0123456789.", *item)) { /* item is a number */
323181786Sdwmalone				double num, offsetnum;
32416186Salex
325181786Sdwmalone				if (quantity)
326181786Sdwmalone					theunit->quantity = 1;
327181786Sdwmalone
328181786Sdwmalone				offset = strchr(item, '&');
329181786Sdwmalone				if (offset) {
330181786Sdwmalone					*offset = 0;
331181786Sdwmalone					offsetnum = atof(offset+1);
332181786Sdwmalone				} else
333181786Sdwmalone					offsetnum = 0.0;
334181786Sdwmalone
33516186Salex				divider = strchr(item, '|');
33616186Salex				if (divider) {
33716186Salex					*divider = 0;
33816186Salex					num = atof(item);
33916186Salex					if (!num) {
34016186Salex						zeroerror();
34116186Salex						return 1;
34216186Salex					}
343181786Sdwmalone					if (doingtop ^ flip) {
34416186Salex						theunit->factor *= num;
345181786Sdwmalone						theunit->offset *= num;
346181786Sdwmalone					} else {
34716186Salex						theunit->factor /= num;
348181786Sdwmalone						theunit->offset /= num;
349181786Sdwmalone					}
35016186Salex					num = atof(divider + 1);
35116186Salex					if (!num) {
35216186Salex						zeroerror();
35316186Salex						return 1;
35416186Salex					}
355181786Sdwmalone					if (doingtop ^ flip) {
35616186Salex						theunit->factor /= num;
357181786Sdwmalone						theunit->offset /= num;
358181786Sdwmalone					} else {
35916186Salex						theunit->factor *= num;
360181786Sdwmalone						theunit->offset *= num;
361181786Sdwmalone					}
36216186Salex				}
36316186Salex				else {
36416186Salex					num = atof(item);
36516186Salex					if (!num) {
36616186Salex						zeroerror();
36716186Salex						return 1;
36816186Salex					}
369181786Sdwmalone					if (doingtop ^ flip) {
37016186Salex						theunit->factor *= num;
371181786Sdwmalone						theunit->offset *= num;
372181786Sdwmalone					} else {
37316186Salex						theunit->factor /= num;
374181786Sdwmalone						theunit->offset /= num;
375181786Sdwmalone					}
37616186Salex				}
377181786Sdwmalone				if (doingtop ^ flip)
378181786Sdwmalone					theunit->offset += offsetnum;
37916186Salex			}
38016186Salex			else {	/* item is not a number */
38116186Salex				int repeat = 1;
38216186Salex
38316186Salex				if (strchr("23456789",
38416186Salex				    item[strlen(item) - 1])) {
38516186Salex					repeat = item[strlen(item) - 1] - '0';
38616186Salex					item[strlen(item) - 1] = 0;
38716186Salex				}
38816186Salex				for (; repeat; repeat--)
38916186Salex					if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
39016186Salex						return 1;
39116186Salex			}
39216186Salex			item = strtok(NULL, " *\t/\n");
39316186Salex		}
39416186Salex		doingtop--;
39516186Salex		if (slash) {
39616186Salex			scratch = slash + 1;
39716186Salex		}
39816186Salex		else
39916186Salex			doingtop--;
40016186Salex	} while (doingtop >= 0);
40116186Salex	free(savescr);
40216186Salex	return 0;
40316186Salex}
40416186Salex
40516186Salex
40616186Salexint
40716186Salexcompare(const void *item1, const void *item2)
40816186Salex{
409100826Sdwmalone	return strcmp(*(const char * const *)item1, *(const char * const *)item2);
41016186Salex}
41116186Salex
41216186Salex
41316186Salexvoid
41416186Salexsortunit(struct unittype * theunit)
41516186Salex{
41616186Salex	char **ptr;
41773229Sdwmalone	unsigned int count;
41816186Salex
41916186Salex	for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
42016186Salex	qsort(theunit->numerator, count, sizeof(char *), compare);
42116186Salex	for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
42216186Salex	qsort(theunit->denominator, count, sizeof(char *), compare);
42316186Salex}
42416186Salex
42516186Salex
42616186Salexvoid
42716186Salexcancelunit(struct unittype * theunit)
42816186Salex{
42916186Salex	char **den, **num;
43016186Salex	int comp;
43116186Salex
43216186Salex	den = theunit->denominator;
43316186Salex	num = theunit->numerator;
43416186Salex
43516186Salex	while (*num && *den) {
43616186Salex		comp = strcmp(*den, *num);
43716186Salex		if (!comp) {
43816186Salex/*      if (*den!=NULLUNIT) free(*den);
43916186Salex      if (*num!=NULLUNIT) free(*num);*/
44016186Salex			*den++ = NULLUNIT;
44116186Salex			*num++ = NULLUNIT;
44216186Salex		}
44316186Salex		else if (comp < 0)
44416186Salex			den++;
44516186Salex		else
44616186Salex			num++;
44716186Salex	}
44816186Salex}
44916186Salex
45016186Salex
45116186Salex
45216186Salex
45316186Salex/*
45416186Salex   Looks up the definition for the specified unit.
45516186Salex   Returns a pointer to the definition or a null pointer
45616186Salex   if the specified unit does not appear in the units table.
45716186Salex*/
45816186Salex
45916186Salexstatic char buffer[100];	/* buffer for lookupunit answers with
46016186Salex				   prefixes */
46116186Salex
46216186Salexchar *
46373229Sdwmalonelookupunit(const char *unit)
46416186Salex{
46516186Salex	int i;
46616186Salex	char *copy;
46716186Salex
46816186Salex	for (i = 0; i < unitcount; i++) {
46916186Salex		if (!strcmp(unittable[i].uname, unit))
47016186Salex			return unittable[i].uval;
47116186Salex	}
47216186Salex
47316186Salex	if (unit[strlen(unit) - 1] == '^') {
47416186Salex		copy = dupstr(unit);
47516186Salex		copy[strlen(copy) - 1] = 0;
47616186Salex		for (i = 0; i < unitcount; i++) {
47716186Salex			if (!strcmp(unittable[i].uname, copy)) {
47864053Skris				strlcpy(buffer, copy, sizeof(buffer));
47916186Salex				free(copy);
48016186Salex				return buffer;
48116186Salex			}
48216186Salex		}
48316186Salex		free(copy);
48416186Salex	}
48516186Salex	if (unit[strlen(unit) - 1] == 's') {
48616186Salex		copy = dupstr(unit);
48716186Salex		copy[strlen(copy) - 1] = 0;
48816186Salex		for (i = 0; i < unitcount; i++) {
48916186Salex			if (!strcmp(unittable[i].uname, copy)) {
49064053Skris				strlcpy(buffer, copy, sizeof(buffer));
49116186Salex				free(copy);
49216186Salex				return buffer;
49316186Salex			}
49416186Salex		}
49516186Salex		if (copy[strlen(copy) - 1] == 'e') {
49616186Salex			copy[strlen(copy) - 1] = 0;
49716186Salex			for (i = 0; i < unitcount; i++) {
49816186Salex				if (!strcmp(unittable[i].uname, copy)) {
49964053Skris					strlcpy(buffer, copy, sizeof(buffer));
50016186Salex					free(copy);
50116186Salex					return buffer;
50216186Salex				}
50316186Salex			}
50416186Salex		}
50516186Salex		free(copy);
50616186Salex	}
50716186Salex	for (i = 0; i < prefixcount; i++) {
50873229Sdwmalone		size_t len = strlen(prefixtable[i].prefixname);
50973229Sdwmalone		if (!strncmp(prefixtable[i].prefixname, unit, len)) {
51073229Sdwmalone			if (!strlen(unit + len) || lookupunit(unit + len)) {
51164053Skris				snprintf(buffer, sizeof(buffer), "%s %s",
51273229Sdwmalone				    prefixtable[i].prefixval, unit + len);
51316186Salex				return buffer;
51416186Salex			}
51516186Salex		}
51616186Salex	}
51716186Salex	return 0;
51816186Salex}
51916186Salex
52016186Salex
52116186Salex
52216186Salex/*
52316186Salex   reduces a product of symbolic units to primitive units.
52416186Salex   The three low bits are used to return flags:
52516186Salex
52616186Salex     bit 0 (1) set on if reductions were performed without error.
52716186Salex     bit 1 (2) set on if no reductions are performed.
52816186Salex     bit 2 (4) set on if an unknown unit is discovered.
52916186Salex*/
53016186Salex
53116186Salex
53216186Salex#define ERROR 4
53316186Salex
53416186Salexint
53516186Salexreduceproduct(struct unittype * theunit, int flip)
53616186Salex{
53716186Salex
53816186Salex	char *toadd;
53916186Salex	char **product;
54016186Salex	int didsomething = 2;
54116186Salex
54216186Salex	if (flip)
54316186Salex		product = theunit->denominator;
54416186Salex	else
54516186Salex		product = theunit->numerator;
54616186Salex
54716186Salex	for (; *product; product++) {
54816186Salex
54916186Salex		for (;;) {
55016186Salex			if (!strlen(*product))
55116186Salex				break;
55216186Salex			toadd = lookupunit(*product);
55316186Salex			if (!toadd) {
55416186Salex				printf("unknown unit '%s'\n", *product);
55516186Salex				return ERROR;
55616186Salex			}
55716186Salex			if (strchr(toadd, PRIMITIVECHAR))
55816186Salex				break;
55916186Salex			didsomething = 1;
56016186Salex			if (*product != NULLUNIT) {
56116186Salex				free(*product);
56216186Salex				*product = NULLUNIT;
56316186Salex			}
564181786Sdwmalone			if (addunit(theunit, toadd, flip, 0))
56516186Salex				return ERROR;
56616186Salex		}
56716186Salex	}
56816186Salex	return didsomething;
56916186Salex}
57016186Salex
57116186Salex
57216186Salex/*
57316186Salex   Reduces numerator and denominator of the specified unit.
57416186Salex   Returns 0 on success, or 1 on unknown unit error.
57516186Salex*/
57616186Salex
57716186Salexint
57816186Salexreduceunit(struct unittype * theunit)
57916186Salex{
58016186Salex	int ret;
58116186Salex
58216186Salex	ret = 1;
58316186Salex	while (ret & 1) {
58416186Salex		ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
58516186Salex		if (ret & 4)
58616186Salex			return 1;
58716186Salex	}
58816186Salex	return 0;
58916186Salex}
59016186Salex
59116186Salex
59216186Salexint
59316186Salexcompareproducts(char **one, char **two)
59416186Salex{
59516186Salex	while (*one || *two) {
59616186Salex		if (!*one && *two != NULLUNIT)
59716186Salex			return 1;
59816186Salex		if (!*two && *one != NULLUNIT)
59916186Salex			return 1;
60016186Salex		if (*one == NULLUNIT)
60116186Salex			one++;
60216186Salex		else if (*two == NULLUNIT)
60316186Salex			two++;
60416186Salex		else if (strcmp(*one, *two))
60516186Salex			return 1;
60616186Salex		else
60716186Salex			one++, two++;
60816186Salex	}
60916186Salex	return 0;
61016186Salex}
61116186Salex
61216186Salex
61316186Salex/* Return zero if units are compatible, nonzero otherwise */
61416186Salex
61516186Salexint
61616186Salexcompareunits(struct unittype * first, struct unittype * second)
61716186Salex{
61816186Salex	return
61916186Salex	compareproducts(first->numerator, second->numerator) ||
62016186Salex	compareproducts(first->denominator, second->denominator);
62116186Salex}
62216186Salex
62316186Salex
62416186Salexint
62516186Salexcompletereduce(struct unittype * unit)
62616186Salex{
62716186Salex	if (reduceunit(unit))
62816186Salex		return 1;
62916186Salex	sortunit(unit);
63016186Salex	cancelunit(unit);
63116186Salex	return 0;
63216186Salex}
63316186Salex
63416186Salex
63516186Salexvoid
63616186Salexshowanswer(struct unittype * have, struct unittype * want)
63716186Salex{
63816186Salex	if (compareunits(have, want)) {
63916186Salex		printf("conformability error\n");
64016186Salex		showunit(have);
64116186Salex		showunit(want);
64216186Salex	}
643181786Sdwmalone	else if (have->offset != want->offset) {
644181786Sdwmalone		if (want->quantity)
645181786Sdwmalone			printf("WARNING: conversion of non-proportional quantities.\n");
646181786Sdwmalone		printf("\t");
647181786Sdwmalone		if (have->quantity)
648181786Sdwmalone			printf("%.8g\n",
649181786Sdwmalone			    (have->factor + have->offset-want->offset)/want->factor);
650181786Sdwmalone		else
651181786Sdwmalone			printf(" (-> x*%.8g %+.8g)\n\t (<- y*%.8g %+.8g)\n",
652181786Sdwmalone			    have->factor / want->factor,
653181786Sdwmalone			    (have->offset-want->offset)/want->factor,
654181786Sdwmalone			    want->factor / have->factor,
655181786Sdwmalone			    (want->offset - have->offset)/have->factor);
656181786Sdwmalone	}
65716186Salex	else
65816186Salex		printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
65916186Salex		    want->factor / have->factor);
66016186Salex}
66116186Salex
66216186Salex
66316186Salexvoid
66473229Sdwmaloneusage(void)
66516186Salex{
66628502Scharnier	fprintf(stderr,
66728502Scharnier		"usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
66816186Salex	exit(3);
66916186Salex}
67016186Salex
67116186Salex
67216186Salexint
67316186Salexmain(int argc, char **argv)
67416186Salex{
67516186Salex
67616186Salex	struct unittype have, want;
67716186Salex	char havestr[81], wantstr[81];
67816186Salex	int optchar;
67916186Salex	char *userfile = 0;
68016186Salex	int quiet = 0;
68116186Salex
68216186Salex	while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
68316186Salex		switch (optchar) {
68416186Salex		case 'f':
68516186Salex			userfile = optarg;
68616186Salex			break;
68716186Salex		case 'q':
68816186Salex			quiet = 1;
68916186Salex			break;
69016186Salex		case 'v':
69116186Salex			fprintf(stderr, "\n  units version %s  Copyright (c) 1993 by Adrian Mariano\n",
69216186Salex			    VERSION);
69316186Salex			fprintf(stderr, "                    This program may be freely distributed\n");
69416186Salex			usage();
69516186Salex		default:
69616186Salex			usage();
69716186Salex			break;
69816186Salex		}
69916186Salex	}
70016186Salex
70116186Salex	if (optind != argc - 2 && optind != argc)
70216186Salex		usage();
70316186Salex
70416186Salex	readunits(userfile);
70516186Salex
70616186Salex	if (optind == argc - 2) {
70764053Skris		strlcpy(havestr, argv[optind], sizeof(havestr));
70864053Skris		strlcpy(wantstr, argv[optind + 1], sizeof(wantstr));
70916186Salex		initializeunit(&have);
710181786Sdwmalone		addunit(&have, havestr, 0, 1);
71116186Salex		completereduce(&have);
71216186Salex		initializeunit(&want);
713181786Sdwmalone		addunit(&want, wantstr, 0, 1);
71416186Salex		completereduce(&want);
71516186Salex		showanswer(&have, &want);
71616186Salex	}
71716186Salex	else {
71816186Salex		if (!quiet)
71928502Scharnier			printf("%d units, %d prefixes\n", unitcount,
72016186Salex			    prefixcount);
72116186Salex		for (;;) {
72216186Salex			do {
72316186Salex				initializeunit(&have);
72416186Salex				if (!quiet)
72516186Salex					printf("You have: ");
72673229Sdwmalone				if (!fgets(havestr, sizeof(havestr), stdin)) {
72716190Salex					if (!quiet)
72816190Salex						putchar('\n');
72916186Salex					exit(0);
73016186Salex				}
731181786Sdwmalone			} while (addunit(&have, havestr, 0, 1) ||
73216186Salex			    completereduce(&have));
73316186Salex			do {
73416186Salex				initializeunit(&want);
73516186Salex				if (!quiet)
73616186Salex					printf("You want: ");
73773229Sdwmalone				if (!fgets(wantstr, sizeof(wantstr), stdin)) {
73816186Salex					if (!quiet)
73916186Salex						putchar('\n');
74016186Salex					exit(0);
74116186Salex				}
742181786Sdwmalone			} while (addunit(&want, wantstr, 0, 1) ||
74316186Salex			    completereduce(&want));
74416186Salex			showanswer(&have, &want);
74516186Salex		}
74616186Salex	}
74716190Salex
74816190Salex	return(0);
74916186Salex}
750