xref: /minix3/usr.bin/units/units.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: units.c,v 1.25 2014/01/07 02:07:09 joerg Exp $	*/
22106ea47SThomas Cort 
32106ea47SThomas Cort /*
42106ea47SThomas Cort  * units.c   Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
52106ea47SThomas Cort  *
62106ea47SThomas Cort  * Redistribution and use in source and binary forms, with or without
72106ea47SThomas Cort  * modification, are permitted provided that the following conditions
82106ea47SThomas Cort  * are met:
92106ea47SThomas Cort  * 1. Redistributions of source code must retain the above copyright
102106ea47SThomas Cort  *    notice, this list of conditions and the following disclaimer.
112106ea47SThomas Cort  * 2. The name of the author may not be used to endorse or promote products
122106ea47SThomas Cort  *    derived from this software without specific prior written permission.
132106ea47SThomas Cort  * Disclaimer:  This software is provided by the author "as is".  The author
142106ea47SThomas Cort  * shall not be liable for any damages caused in any way by this software.
152106ea47SThomas Cort  *
162106ea47SThomas Cort  * I would appreciate (though I do not require) receiving a copy of any
172106ea47SThomas Cort  * improvements you might make to this program.
182106ea47SThomas Cort  */
192106ea47SThomas Cort 
202106ea47SThomas Cort #include <ctype.h>
212106ea47SThomas Cort #include <err.h>
222106ea47SThomas Cort #include <float.h>
232106ea47SThomas Cort #include <stdio.h>
242106ea47SThomas Cort #include <string.h>
252106ea47SThomas Cort #include <stdlib.h>
262106ea47SThomas Cort #include <unistd.h>
272106ea47SThomas Cort 
282106ea47SThomas Cort #include "pathnames.h"
292106ea47SThomas Cort 
302106ea47SThomas Cort #define VERSION "1.0"
312106ea47SThomas Cort 
322106ea47SThomas Cort #ifndef UNITSFILE
332106ea47SThomas Cort #define UNITSFILE _PATH_UNITSLIB
342106ea47SThomas Cort #endif
352106ea47SThomas Cort 
362106ea47SThomas Cort #define MAXUNITS 1000
372106ea47SThomas Cort #define MAXPREFIXES 50
382106ea47SThomas Cort 
392106ea47SThomas Cort #define MAXSUBUNITS 500
402106ea47SThomas Cort 
412106ea47SThomas Cort #define PRIMITIVECHAR '!'
422106ea47SThomas Cort 
432106ea47SThomas Cort static int precision = 8;		/* for printf with "%.*g" format */
442106ea47SThomas Cort 
452106ea47SThomas Cort static const char *errprefix = NULL;	/* if not NULL, then prepend this
462106ea47SThomas Cort 					 * to error messages and send them to
472106ea47SThomas Cort 					 * stdout instead of stderr.
482106ea47SThomas Cort 					 */
492106ea47SThomas Cort 
502106ea47SThomas Cort static const char *powerstring = "^";
512106ea47SThomas Cort 
522106ea47SThomas Cort static struct {
532106ea47SThomas Cort 	const char *uname;
542106ea47SThomas Cort 	const char *uval;
552106ea47SThomas Cort }      unittable[MAXUNITS];
562106ea47SThomas Cort 
572106ea47SThomas Cort struct unittype {
582106ea47SThomas Cort 	const char *numerator[MAXSUBUNITS];
592106ea47SThomas Cort 	const char *denominator[MAXSUBUNITS];
602106ea47SThomas Cort 	double factor;
612106ea47SThomas Cort };
622106ea47SThomas Cort 
632106ea47SThomas Cort struct {
642106ea47SThomas Cort 	const char *prefixname;
652106ea47SThomas Cort 	const char *prefixval;
662106ea47SThomas Cort }      prefixtable[MAXPREFIXES];
672106ea47SThomas Cort 
682106ea47SThomas Cort 
692106ea47SThomas Cort static const char *NULLUNIT = "";
702106ea47SThomas Cort 
712106ea47SThomas Cort static int unitcount;
722106ea47SThomas Cort static int prefixcount;
732106ea47SThomas Cort 
742106ea47SThomas Cort 
752106ea47SThomas Cort static int	addsubunit(const char *[], const char *);
762106ea47SThomas Cort static int	addunit(struct unittype *, const char *, int);
772106ea47SThomas Cort static void	cancelunit(struct unittype *);
782106ea47SThomas Cort static int	compare(const void *, const void *);
792106ea47SThomas Cort static int	compareproducts(const char **, const char **);
802106ea47SThomas Cort static int	compareunits(struct unittype *, struct unittype *);
812106ea47SThomas Cort static int	compareunitsreciprocal(struct unittype *, struct unittype *);
822106ea47SThomas Cort static int	completereduce(struct unittype *);
832106ea47SThomas Cort static void	initializeunit(struct unittype *);
842106ea47SThomas Cort static void	readerror(int);
852106ea47SThomas Cort static void	readunits(const char *);
862106ea47SThomas Cort static int	reduceproduct(struct unittype *, int);
872106ea47SThomas Cort static int	reduceunit(struct unittype *);
882106ea47SThomas Cort static void	showanswer(struct unittype *, struct unittype *);
892106ea47SThomas Cort static void	showunit(struct unittype *);
902106ea47SThomas Cort static void	sortunit(struct unittype *);
912106ea47SThomas Cort __dead static void	usage(void);
922106ea47SThomas Cort static void	zeroerror(void);
932106ea47SThomas Cort static char   *dupstr(const char *);
942106ea47SThomas Cort static const char *lookupunit(const char *);
952106ea47SThomas Cort 
962106ea47SThomas Cort static char *
dupstr(const char * str)972106ea47SThomas Cort dupstr(const char *str)
982106ea47SThomas Cort {
992106ea47SThomas Cort 	char *ret;
1002106ea47SThomas Cort 
1012106ea47SThomas Cort 	ret = strdup(str);
1022106ea47SThomas Cort 	if (!ret)
1032106ea47SThomas Cort 		err(3, "Memory allocation error");
1042106ea47SThomas Cort 	return (ret);
1052106ea47SThomas Cort }
1062106ea47SThomas Cort 
1072106ea47SThomas Cort 
108*0a6a1f1dSLionel Sambuc static __printflike(1, 2) void
mywarnx(const char * fmt,...)1092106ea47SThomas Cort mywarnx(const char *fmt, ...)
1102106ea47SThomas Cort {
1112106ea47SThomas Cort 	va_list args;
1122106ea47SThomas Cort 
1132106ea47SThomas Cort 	va_start(args, fmt);
1142106ea47SThomas Cort 	if (errprefix) {
1152106ea47SThomas Cort 		/* warn to stdout, with errprefix prepended */
1162106ea47SThomas Cort 		printf("%s", errprefix);
1172106ea47SThomas Cort 		vprintf(fmt, args);
1182106ea47SThomas Cort 		printf("%s", "\n");
1192106ea47SThomas Cort 	} else {
1202106ea47SThomas Cort 		/* warn to stderr */
1212106ea47SThomas Cort 		vwarnx(fmt, args);
1222106ea47SThomas Cort 	}
1232106ea47SThomas Cort 	va_end(args);
1242106ea47SThomas Cort }
1252106ea47SThomas Cort 
1262106ea47SThomas Cort static void
readerror(int linenum)1272106ea47SThomas Cort readerror(int linenum)
1282106ea47SThomas Cort {
1292106ea47SThomas Cort 	mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum);
1302106ea47SThomas Cort }
1312106ea47SThomas Cort 
1322106ea47SThomas Cort 
1332106ea47SThomas Cort static void
readunits(const char * userfile)1342106ea47SThomas Cort readunits(const char *userfile)
1352106ea47SThomas Cort {
1362106ea47SThomas Cort 	FILE *unitfile;
1372106ea47SThomas Cort 	char line[80], *lineptr;
1382106ea47SThomas Cort 	int len, linenum, i, isdup;
1392106ea47SThomas Cort 
1402106ea47SThomas Cort 	unitcount = 0;
1412106ea47SThomas Cort 	linenum = 0;
1422106ea47SThomas Cort 
1432106ea47SThomas Cort 	if (userfile) {
1442106ea47SThomas Cort 		unitfile = fopen(userfile, "rt");
1452106ea47SThomas Cort 		if (!unitfile)
1462106ea47SThomas Cort 			err(1, "Unable to open units file '%s'", userfile);
1472106ea47SThomas Cort 	}
1482106ea47SThomas Cort 	else {
1492106ea47SThomas Cort 		unitfile = fopen(UNITSFILE, "rt");
1502106ea47SThomas Cort 		if (!unitfile) {
1512106ea47SThomas Cort 			char *direc, *env;
1522106ea47SThomas Cort 			char filename[1000];
1532106ea47SThomas Cort 			char separator[2];
1542106ea47SThomas Cort 
1552106ea47SThomas Cort 			env = getenv("PATH");
1562106ea47SThomas Cort 			if (env) {
1572106ea47SThomas Cort 				if (strchr(env, ';'))
1582106ea47SThomas Cort 					strlcpy(separator, ";",
1592106ea47SThomas Cort 					    sizeof(separator));
1602106ea47SThomas Cort 				else
1612106ea47SThomas Cort 					strlcpy(separator, ":",
1622106ea47SThomas Cort 					    sizeof(separator));
1632106ea47SThomas Cort 				direc = strtok(env, separator);
1642106ea47SThomas Cort 				while (direc) {
1652106ea47SThomas Cort 					strlcpy(filename, "", sizeof(filename));
1662106ea47SThomas Cort 					strlcat(filename, direc,
1672106ea47SThomas Cort 					    sizeof(filename));
1682106ea47SThomas Cort 					strlcat(filename, "/",
1692106ea47SThomas Cort 					    sizeof(filename));
1702106ea47SThomas Cort 					strlcat(filename, UNITSFILE,
1712106ea47SThomas Cort 					    sizeof(filename));
1722106ea47SThomas Cort 					unitfile = fopen(filename, "rt");
1732106ea47SThomas Cort 					if (unitfile)
1742106ea47SThomas Cort 						break;
1752106ea47SThomas Cort 					direc = strtok(NULL, separator);
1762106ea47SThomas Cort 				}
1772106ea47SThomas Cort 			}
1782106ea47SThomas Cort 			if (!unitfile)
1792106ea47SThomas Cort 				errx(1, "Can't find units file '%s'",
1802106ea47SThomas Cort 				    UNITSFILE);
1812106ea47SThomas Cort 		}
1822106ea47SThomas Cort 	}
1832106ea47SThomas Cort 	while (!feof(unitfile)) {
1842106ea47SThomas Cort 		if (!fgets(line, 79, unitfile))
1852106ea47SThomas Cort 			break;
1862106ea47SThomas Cort 		linenum++;
1872106ea47SThomas Cort 		lineptr = line;
1882106ea47SThomas Cort 		if (*lineptr == '/')
1892106ea47SThomas Cort 			continue;
1902106ea47SThomas Cort 		lineptr += strspn(lineptr, " \n\t");
1912106ea47SThomas Cort 		len = strcspn(lineptr, " \n\t");
1922106ea47SThomas Cort 		lineptr[len] = 0;
1932106ea47SThomas Cort 		if (!strlen(lineptr))
1942106ea47SThomas Cort 			continue;
1952106ea47SThomas Cort 		if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
1962106ea47SThomas Cort 			if (prefixcount == MAXPREFIXES) {
1972106ea47SThomas Cort 				mywarnx(
1982106ea47SThomas Cort 			"Memory for prefixes exceeded in line %d",
1992106ea47SThomas Cort 					linenum);
2002106ea47SThomas Cort 				continue;
2012106ea47SThomas Cort 			}
2022106ea47SThomas Cort 			lineptr[strlen(lineptr) - 1] = 0;
2032106ea47SThomas Cort 			for (isdup = 0, i = 0; i < prefixcount; i++) {
2042106ea47SThomas Cort 				if (!strcmp(prefixtable[i].prefixname,
2052106ea47SThomas Cort 				    lineptr)) {
2062106ea47SThomas Cort 					isdup = 1;
2072106ea47SThomas Cort 					break;
2082106ea47SThomas Cort 				}
2092106ea47SThomas Cort 			}
2102106ea47SThomas Cort 			if (isdup) {
2112106ea47SThomas Cort 				mywarnx(
2122106ea47SThomas Cort 			"Redefinition of prefix '%s' on line %d ignored",
2132106ea47SThomas Cort 				    lineptr, linenum);
2142106ea47SThomas Cort 				continue;
2152106ea47SThomas Cort 			}
2162106ea47SThomas Cort 			prefixtable[prefixcount].prefixname = dupstr(lineptr);
2172106ea47SThomas Cort 			lineptr += len + 1;
2182106ea47SThomas Cort 			if (!strlen(lineptr)) {
2192106ea47SThomas Cort 				readerror(linenum);
2202106ea47SThomas Cort 				continue;
2212106ea47SThomas Cort 			}
2222106ea47SThomas Cort 			lineptr += strspn(lineptr, " \n\t");
2232106ea47SThomas Cort 			len = strcspn(lineptr, "\n\t");
2242106ea47SThomas Cort 			lineptr[len] = 0;
2252106ea47SThomas Cort 			prefixtable[prefixcount++].prefixval = dupstr(lineptr);
2262106ea47SThomas Cort 		}
2272106ea47SThomas Cort 		else {		/* it's not a prefix */
2282106ea47SThomas Cort 			if (unitcount == MAXUNITS) {
2292106ea47SThomas Cort 				mywarnx("Memory for units exceeded in line %d",
2302106ea47SThomas Cort 				    linenum);
2312106ea47SThomas Cort 				continue;
2322106ea47SThomas Cort 			}
2332106ea47SThomas Cort 			for (isdup = 0, i = 0; i < unitcount; i++) {
2342106ea47SThomas Cort 				if (!strcmp(unittable[i].uname, lineptr)) {
2352106ea47SThomas Cort 					isdup = 1;
2362106ea47SThomas Cort 					break;
2372106ea47SThomas Cort 				}
2382106ea47SThomas Cort 			}
2392106ea47SThomas Cort 			if (isdup) {
2402106ea47SThomas Cort 				mywarnx(
2412106ea47SThomas Cort 				"Redefinition of unit '%s' on line %d ignored",
2422106ea47SThomas Cort 				    lineptr, linenum);
2432106ea47SThomas Cort 				continue;
2442106ea47SThomas Cort 			}
2452106ea47SThomas Cort 			unittable[unitcount].uname = dupstr(lineptr);
2462106ea47SThomas Cort 			lineptr += len + 1;
2472106ea47SThomas Cort 			lineptr += strspn(lineptr, " \n\t");
2482106ea47SThomas Cort 			if (!strlen(lineptr)) {
2492106ea47SThomas Cort 				readerror(linenum);
2502106ea47SThomas Cort 				continue;
2512106ea47SThomas Cort 			}
2522106ea47SThomas Cort 			len = strcspn(lineptr, "\n\t");
2532106ea47SThomas Cort 			lineptr[len] = 0;
2542106ea47SThomas Cort 			unittable[unitcount++].uval = dupstr(lineptr);
2552106ea47SThomas Cort 		}
2562106ea47SThomas Cort 	}
2572106ea47SThomas Cort 	fclose(unitfile);
2582106ea47SThomas Cort }
2592106ea47SThomas Cort 
2602106ea47SThomas Cort static void
initializeunit(struct unittype * theunit)2612106ea47SThomas Cort initializeunit(struct unittype * theunit)
2622106ea47SThomas Cort {
2632106ea47SThomas Cort 	theunit->factor = 1.0;
2642106ea47SThomas Cort 	theunit->numerator[0] = theunit->denominator[0] = NULL;
2652106ea47SThomas Cort }
2662106ea47SThomas Cort 
2672106ea47SThomas Cort static int
addsubunit(const char * product[],const char * toadd)2682106ea47SThomas Cort addsubunit(const char *product[], const char *toadd)
2692106ea47SThomas Cort {
2702106ea47SThomas Cort 	const char **ptr;
2712106ea47SThomas Cort 
2722106ea47SThomas Cort 	for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
2732106ea47SThomas Cort 	if (ptr >= product + MAXSUBUNITS) {
2742106ea47SThomas Cort 		mywarnx("Memory overflow in unit reduction");
2752106ea47SThomas Cort 		return 1;
2762106ea47SThomas Cort 	}
2772106ea47SThomas Cort 	if (!*ptr)
2782106ea47SThomas Cort 		*(ptr + 1) = 0;
2792106ea47SThomas Cort 	*ptr = dupstr(toadd);
2802106ea47SThomas Cort 	return 0;
2812106ea47SThomas Cort }
2822106ea47SThomas Cort 
2832106ea47SThomas Cort static void
showunit(struct unittype * theunit)2842106ea47SThomas Cort showunit(struct unittype * theunit)
2852106ea47SThomas Cort {
2862106ea47SThomas Cort 	const char **ptr;
2872106ea47SThomas Cort 	int printedslash;
2882106ea47SThomas Cort 	int counter = 1;
2892106ea47SThomas Cort 
2902106ea47SThomas Cort 	printf("\t%.*g", precision, theunit->factor);
2912106ea47SThomas Cort 	for (ptr = theunit->numerator; *ptr; ptr++) {
2922106ea47SThomas Cort 		if (ptr > theunit->numerator && **ptr &&
2932106ea47SThomas Cort 		    !strcmp(*ptr, *(ptr - 1)))
2942106ea47SThomas Cort 			counter++;
2952106ea47SThomas Cort 		else {
2962106ea47SThomas Cort 			if (counter > 1)
2972106ea47SThomas Cort 				printf("%s%d", powerstring, counter);
2982106ea47SThomas Cort 			if (**ptr)
2992106ea47SThomas Cort 				printf(" %s", *ptr);
3002106ea47SThomas Cort 			counter = 1;
3012106ea47SThomas Cort 		}
3022106ea47SThomas Cort 	}
3032106ea47SThomas Cort 	if (counter > 1)
3042106ea47SThomas Cort 		printf("%s%d", powerstring, counter);
3052106ea47SThomas Cort 	counter = 1;
3062106ea47SThomas Cort 	printedslash = 0;
3072106ea47SThomas Cort 	for (ptr = theunit->denominator; *ptr; ptr++) {
3082106ea47SThomas Cort 		if (ptr > theunit->denominator && **ptr &&
3092106ea47SThomas Cort 		    !strcmp(*ptr, *(ptr - 1)))
3102106ea47SThomas Cort 			counter++;
3112106ea47SThomas Cort 		else {
3122106ea47SThomas Cort 			if (counter > 1)
3132106ea47SThomas Cort 				printf("%s%d", powerstring, counter);
3142106ea47SThomas Cort 			if (**ptr) {
3152106ea47SThomas Cort 				if (!printedslash)
3162106ea47SThomas Cort 					printf(" /");
3172106ea47SThomas Cort 				printedslash = 1;
3182106ea47SThomas Cort 				printf(" %s", *ptr);
3192106ea47SThomas Cort 			}
3202106ea47SThomas Cort 			counter = 1;
3212106ea47SThomas Cort 		}
3222106ea47SThomas Cort 	}
3232106ea47SThomas Cort 	if (counter > 1)
3242106ea47SThomas Cort 		printf("%s%d", powerstring, counter);
3252106ea47SThomas Cort 	printf("\n");
3262106ea47SThomas Cort }
3272106ea47SThomas Cort 
3282106ea47SThomas Cort static void
zeroerror(void)3292106ea47SThomas Cort zeroerror(void)
3302106ea47SThomas Cort {
3312106ea47SThomas Cort 	mywarnx("Unit reduces to zero");
3322106ea47SThomas Cort }
3332106ea47SThomas Cort 
3342106ea47SThomas Cort /*
3352106ea47SThomas Cort    Adds the specified string to the unit.
3362106ea47SThomas Cort    Flip is 0 for adding normally, 1 for adding reciprocal.
3372106ea47SThomas Cort 
3382106ea47SThomas Cort    Returns 0 for successful addition, nonzero on error.
3392106ea47SThomas Cort */
3402106ea47SThomas Cort 
3412106ea47SThomas Cort static int
addunit(struct unittype * theunit,const char * toadd,int flip)3422106ea47SThomas Cort addunit(struct unittype * theunit, const char *toadd, int flip)
3432106ea47SThomas Cort {
3442106ea47SThomas Cort 	char *scratch, *savescr;
3452106ea47SThomas Cort 	char *item;
3462106ea47SThomas Cort 	char *divider, *slash;
3472106ea47SThomas Cort 	int doingtop;
3482106ea47SThomas Cort 
3492106ea47SThomas Cort 	savescr = scratch = dupstr(toadd);
3502106ea47SThomas Cort 	for (slash = scratch + 1; *slash; slash++)
3512106ea47SThomas Cort 		if (*slash == '-' &&
3522106ea47SThomas Cort 		    (tolower((unsigned char)*(slash - 1)) != 'e' ||
3532106ea47SThomas Cort 		    !strchr(".0123456789", *(slash + 1))))
3542106ea47SThomas Cort 			*slash = ' ';
3552106ea47SThomas Cort 	slash = strchr(scratch, '/');
3562106ea47SThomas Cort 	if (slash)
3572106ea47SThomas Cort 		*slash = 0;
3582106ea47SThomas Cort 	doingtop = 1;
3592106ea47SThomas Cort 	do {
3602106ea47SThomas Cort 		item = strtok(scratch, " *\t\n/");
3612106ea47SThomas Cort 		while (item) {
3622106ea47SThomas Cort 			if (strchr("0123456789.", *item)) {
3632106ea47SThomas Cort 				/* item starts with a number */
3642106ea47SThomas Cort 				char *endptr;
3652106ea47SThomas Cort 				double num;
3662106ea47SThomas Cort 
3672106ea47SThomas Cort 				divider = strchr(item, '|');
3682106ea47SThomas Cort 				if (divider) {
3692106ea47SThomas Cort 					*divider = 0;
3702106ea47SThomas Cort 					num = strtod(item, &endptr);
3712106ea47SThomas Cort 					if (!num) {
3722106ea47SThomas Cort 						zeroerror();
3732106ea47SThomas Cort 						return 1;
3742106ea47SThomas Cort 					}
3752106ea47SThomas Cort 					if (endptr != divider) {
3762106ea47SThomas Cort 						/* "6foo|2" is an error */
3772106ea47SThomas Cort 						mywarnx("Junk before '|'");
3782106ea47SThomas Cort 						return 1;
3792106ea47SThomas Cort 					}
3802106ea47SThomas Cort 					if (doingtop ^ flip)
3812106ea47SThomas Cort 						theunit->factor *= num;
3822106ea47SThomas Cort 					else
3832106ea47SThomas Cort 						theunit->factor /= num;
3842106ea47SThomas Cort 					num = strtod(divider + 1, &endptr);
3852106ea47SThomas Cort 					if (!num) {
3862106ea47SThomas Cort 						zeroerror();
3872106ea47SThomas Cort 						return 1;
3882106ea47SThomas Cort 					}
3892106ea47SThomas Cort 					if (doingtop ^ flip)
3902106ea47SThomas Cort 						theunit->factor /= num;
3912106ea47SThomas Cort 					else
3922106ea47SThomas Cort 						theunit->factor *= num;
3932106ea47SThomas Cort 					if (*endptr) {
3942106ea47SThomas Cort 						/* "6|2foo" is like "6|2 foo" */
3952106ea47SThomas Cort 						item = endptr;
3962106ea47SThomas Cort 						continue;
3972106ea47SThomas Cort 					}
3982106ea47SThomas Cort 				}
3992106ea47SThomas Cort 				else {
4002106ea47SThomas Cort 					num = strtod(item, &endptr);
4012106ea47SThomas Cort 					if (!num) {
4022106ea47SThomas Cort 						zeroerror();
4032106ea47SThomas Cort 						return 1;
4042106ea47SThomas Cort 					}
4052106ea47SThomas Cort 					if (doingtop ^ flip)
4062106ea47SThomas Cort 						theunit->factor *= num;
4072106ea47SThomas Cort 					else
4082106ea47SThomas Cort 						theunit->factor /= num;
4092106ea47SThomas Cort 					if (*endptr) {
4102106ea47SThomas Cort 						/* "3foo" is like "3 foo" */
4112106ea47SThomas Cort 						item = endptr;
4122106ea47SThomas Cort 						continue;
4132106ea47SThomas Cort 					}
4142106ea47SThomas Cort 				}
4152106ea47SThomas Cort 			}
4162106ea47SThomas Cort 			else {	/* item is not a number */
4172106ea47SThomas Cort 				int repeat = 1;
4182106ea47SThomas Cort 
4192106ea47SThomas Cort 				if (strchr("23456789",
4202106ea47SThomas Cort 				    item[strlen(item) - 1])) {
4212106ea47SThomas Cort 					repeat = item[strlen(item) - 1] - '0';
4222106ea47SThomas Cort 					item[strlen(item) - 1] = 0;
4232106ea47SThomas Cort 				}
4242106ea47SThomas Cort 				for (; repeat; repeat--)
4252106ea47SThomas Cort 					if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
4262106ea47SThomas Cort 						return 1;
4272106ea47SThomas Cort 			}
4282106ea47SThomas Cort 			item = strtok(NULL, " *\t/\n");
4292106ea47SThomas Cort 		}
4302106ea47SThomas Cort 		doingtop--;
4312106ea47SThomas Cort 		if (slash) {
4322106ea47SThomas Cort 			scratch = slash + 1;
4332106ea47SThomas Cort 		}
4342106ea47SThomas Cort 		else
4352106ea47SThomas Cort 			doingtop--;
4362106ea47SThomas Cort 	} while (doingtop >= 0);
4372106ea47SThomas Cort 	free(savescr);
4382106ea47SThomas Cort 	return 0;
4392106ea47SThomas Cort }
4402106ea47SThomas Cort 
4412106ea47SThomas Cort static int
compare(const void * item1,const void * item2)4422106ea47SThomas Cort compare(const void *item1, const void *item2)
4432106ea47SThomas Cort {
4442106ea47SThomas Cort 	return strcmp(*(const char * const *) item1,
4452106ea47SThomas Cort 		      *(const char * const *) item2);
4462106ea47SThomas Cort }
4472106ea47SThomas Cort 
4482106ea47SThomas Cort static void
sortunit(struct unittype * theunit)4492106ea47SThomas Cort sortunit(struct unittype * theunit)
4502106ea47SThomas Cort {
4512106ea47SThomas Cort 	const char **ptr;
4522106ea47SThomas Cort 	int count;
4532106ea47SThomas Cort 
4542106ea47SThomas Cort 	for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
4552106ea47SThomas Cort 	qsort(theunit->numerator, count, sizeof(char *), compare);
4562106ea47SThomas Cort 	for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
4572106ea47SThomas Cort 	qsort(theunit->denominator, count, sizeof(char *), compare);
4582106ea47SThomas Cort }
4592106ea47SThomas Cort 
4602106ea47SThomas Cort static void
cancelunit(struct unittype * theunit)4612106ea47SThomas Cort cancelunit(struct unittype * theunit)
4622106ea47SThomas Cort {
4632106ea47SThomas Cort 	const char **den, **num;
4642106ea47SThomas Cort 	int comp;
4652106ea47SThomas Cort 
4662106ea47SThomas Cort 	den = theunit->denominator;
4672106ea47SThomas Cort 	num = theunit->numerator;
4682106ea47SThomas Cort 
4692106ea47SThomas Cort 	while (*num && *den) {
4702106ea47SThomas Cort 		comp = strcmp(*den, *num);
4712106ea47SThomas Cort 		if (!comp) {
4722106ea47SThomas Cort /*      if (*den!=NULLUNIT) free(*den);
4732106ea47SThomas Cort       if (*num!=NULLUNIT) free(*num);*/
4742106ea47SThomas Cort 			*den++ = NULLUNIT;
4752106ea47SThomas Cort 			*num++ = NULLUNIT;
4762106ea47SThomas Cort 		}
4772106ea47SThomas Cort 		else if (comp < 0)
4782106ea47SThomas Cort 			den++;
4792106ea47SThomas Cort 		else
4802106ea47SThomas Cort 			num++;
4812106ea47SThomas Cort 	}
4822106ea47SThomas Cort }
4832106ea47SThomas Cort 
4842106ea47SThomas Cort 
4852106ea47SThomas Cort 
4862106ea47SThomas Cort 
4872106ea47SThomas Cort /*
4882106ea47SThomas Cort    Looks up the definition for the specified unit.
4892106ea47SThomas Cort    Returns a pointer to the definition or a null pointer
4902106ea47SThomas Cort    if the specified unit does not appear in the units table.
4912106ea47SThomas Cort */
4922106ea47SThomas Cort 
4932106ea47SThomas Cort static char buffer[100];	/* buffer for lookupunit answers with
4942106ea47SThomas Cort 				   prefixes */
4952106ea47SThomas Cort 
4962106ea47SThomas Cort static const char *
lookupunit(const char * unit)4972106ea47SThomas Cort lookupunit(const char *unit)
4982106ea47SThomas Cort {
4992106ea47SThomas Cort 	int i;
5002106ea47SThomas Cort 	char *copy;
5012106ea47SThomas Cort 
5022106ea47SThomas Cort 	for (i = 0; i < unitcount; i++) {
5032106ea47SThomas Cort 		if (!strcmp(unittable[i].uname, unit))
5042106ea47SThomas Cort 			return unittable[i].uval;
5052106ea47SThomas Cort 	}
5062106ea47SThomas Cort 
5072106ea47SThomas Cort 	if (unit[strlen(unit) - 1] == '^') {
5082106ea47SThomas Cort 		copy = dupstr(unit);
5092106ea47SThomas Cort 		copy[strlen(copy) - 1] = 0;
5102106ea47SThomas Cort 		for (i = 0; i < unitcount; i++) {
5112106ea47SThomas Cort 			if (!strcmp(unittable[i].uname, copy)) {
5122106ea47SThomas Cort 				strlcpy(buffer, copy, sizeof(buffer));
5132106ea47SThomas Cort 				free(copy);
5142106ea47SThomas Cort 				return buffer;
5152106ea47SThomas Cort 			}
5162106ea47SThomas Cort 		}
5172106ea47SThomas Cort 		free(copy);
5182106ea47SThomas Cort 	}
5192106ea47SThomas Cort 	if (unit[strlen(unit) - 1] == 's') {
5202106ea47SThomas Cort 		copy = dupstr(unit);
5212106ea47SThomas Cort 		copy[strlen(copy) - 1] = 0;
5222106ea47SThomas Cort 		for (i = 0; i < unitcount; i++) {
5232106ea47SThomas Cort 			if (!strcmp(unittable[i].uname, copy)) {
5242106ea47SThomas Cort 				strlcpy(buffer, copy, sizeof(buffer));
5252106ea47SThomas Cort 				free(copy);
5262106ea47SThomas Cort 				return buffer;
5272106ea47SThomas Cort 			}
5282106ea47SThomas Cort 		}
5292106ea47SThomas Cort 		if (copy[strlen(copy) - 1] == 'e') {
5302106ea47SThomas Cort 			copy[strlen(copy) - 1] = 0;
5312106ea47SThomas Cort 			for (i = 0; i < unitcount; i++) {
5322106ea47SThomas Cort 				if (!strcmp(unittable[i].uname, copy)) {
5332106ea47SThomas Cort 					strlcpy(buffer, copy, sizeof(buffer));
5342106ea47SThomas Cort 					free(copy);
5352106ea47SThomas Cort 					return buffer;
5362106ea47SThomas Cort 				}
5372106ea47SThomas Cort 			}
5382106ea47SThomas Cort 		}
5392106ea47SThomas Cort 		free(copy);
5402106ea47SThomas Cort 	}
5412106ea47SThomas Cort 	for (i = 0; i < prefixcount; i++) {
5422106ea47SThomas Cort 		if (!strncmp(prefixtable[i].prefixname, unit,
5432106ea47SThomas Cort 			strlen(prefixtable[i].prefixname))) {
5442106ea47SThomas Cort 			unit += strlen(prefixtable[i].prefixname);
5452106ea47SThomas Cort 			if (!strlen(unit) || lookupunit(unit)) {
5462106ea47SThomas Cort 				strlcpy(buffer, prefixtable[i].prefixval,
5472106ea47SThomas Cort 				    sizeof(buffer));
5482106ea47SThomas Cort 				strlcat(buffer, " ", sizeof(buffer));
5492106ea47SThomas Cort 				strlcat(buffer, unit, sizeof(buffer));
5502106ea47SThomas Cort 				return buffer;
5512106ea47SThomas Cort 			}
5522106ea47SThomas Cort 		}
5532106ea47SThomas Cort 	}
5542106ea47SThomas Cort 	return 0;
5552106ea47SThomas Cort }
5562106ea47SThomas Cort 
5572106ea47SThomas Cort 
5582106ea47SThomas Cort 
5592106ea47SThomas Cort /*
5602106ea47SThomas Cort    reduces a product of symbolic units to primitive units.
5612106ea47SThomas Cort    The three low bits are used to return flags:
5622106ea47SThomas Cort 
5632106ea47SThomas Cort      bit 0 (1) set on if reductions were performed without error.
5642106ea47SThomas Cort      bit 1 (2) set on if no reductions are performed.
5652106ea47SThomas Cort      bit 2 (4) set on if an unknown unit is discovered.
5662106ea47SThomas Cort */
5672106ea47SThomas Cort 
5682106ea47SThomas Cort 
5692106ea47SThomas Cort #define ERROR 4
5702106ea47SThomas Cort 
5712106ea47SThomas Cort static int
reduceproduct(struct unittype * theunit,int flip)5722106ea47SThomas Cort reduceproduct(struct unittype * theunit, int flip)
5732106ea47SThomas Cort {
5742106ea47SThomas Cort 
5752106ea47SThomas Cort 	const char *toadd;
5762106ea47SThomas Cort 	const char **product;
5772106ea47SThomas Cort 	int didsomething = 2;
5782106ea47SThomas Cort 
5792106ea47SThomas Cort 	if (flip)
5802106ea47SThomas Cort 		product = theunit->denominator;
5812106ea47SThomas Cort 	else
5822106ea47SThomas Cort 		product = theunit->numerator;
5832106ea47SThomas Cort 
5842106ea47SThomas Cort 	for (; *product; product++) {
5852106ea47SThomas Cort 
5862106ea47SThomas Cort 		for (;;) {
5872106ea47SThomas Cort 			if (!strlen(*product))
5882106ea47SThomas Cort 				break;
5892106ea47SThomas Cort 			toadd = lookupunit(*product);
5902106ea47SThomas Cort 			if (!toadd) {
5912106ea47SThomas Cort 				mywarnx("Unknown unit '%s'", *product);
5922106ea47SThomas Cort 				return ERROR;
5932106ea47SThomas Cort 			}
5942106ea47SThomas Cort 			if (strchr(toadd, PRIMITIVECHAR))
5952106ea47SThomas Cort 				break;
5962106ea47SThomas Cort 			didsomething = 1;
5972106ea47SThomas Cort 			if (*product != NULLUNIT) {
5982106ea47SThomas Cort 				free(__UNCONST(*product));
5992106ea47SThomas Cort 				*product = NULLUNIT;
6002106ea47SThomas Cort 			}
6012106ea47SThomas Cort 			if (addunit(theunit, toadd, flip))
6022106ea47SThomas Cort 				return ERROR;
6032106ea47SThomas Cort 		}
6042106ea47SThomas Cort 	}
6052106ea47SThomas Cort 	return didsomething;
6062106ea47SThomas Cort }
6072106ea47SThomas Cort 
6082106ea47SThomas Cort 
6092106ea47SThomas Cort /*
6102106ea47SThomas Cort    Reduces numerator and denominator of the specified unit.
6112106ea47SThomas Cort    Returns 0 on success, or 1 on unknown unit error.
6122106ea47SThomas Cort */
6132106ea47SThomas Cort 
6142106ea47SThomas Cort static int
reduceunit(struct unittype * theunit)6152106ea47SThomas Cort reduceunit(struct unittype * theunit)
6162106ea47SThomas Cort {
6172106ea47SThomas Cort 	int ret;
6182106ea47SThomas Cort 
6192106ea47SThomas Cort 	ret = 1;
6202106ea47SThomas Cort 	while (ret & 1) {
6212106ea47SThomas Cort 		ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
6222106ea47SThomas Cort 		if (ret & 4)
6232106ea47SThomas Cort 			return 1;
6242106ea47SThomas Cort 	}
6252106ea47SThomas Cort 	return 0;
6262106ea47SThomas Cort }
6272106ea47SThomas Cort 
6282106ea47SThomas Cort static int
compareproducts(const char ** one,const char ** two)6292106ea47SThomas Cort compareproducts(const char **one, const char **two)
6302106ea47SThomas Cort {
6312106ea47SThomas Cort 	while (*one || *two) {
6322106ea47SThomas Cort 		if (!*one && *two != NULLUNIT)
6332106ea47SThomas Cort 			return 1;
6342106ea47SThomas Cort 		if (!*two && *one != NULLUNIT)
6352106ea47SThomas Cort 			return 1;
6362106ea47SThomas Cort 		if (*one == NULLUNIT)
6372106ea47SThomas Cort 			one++;
6382106ea47SThomas Cort 		else if (*two == NULLUNIT)
6392106ea47SThomas Cort 			two++;
6402106ea47SThomas Cort 		else if (*one && *two && strcmp(*one, *two))
6412106ea47SThomas Cort 			return 1;
6422106ea47SThomas Cort 		else
6432106ea47SThomas Cort 			one++, two++;
6442106ea47SThomas Cort 	}
6452106ea47SThomas Cort 	return 0;
6462106ea47SThomas Cort }
6472106ea47SThomas Cort 
6482106ea47SThomas Cort 
6492106ea47SThomas Cort /* Return zero if units are compatible, nonzero otherwise */
6502106ea47SThomas Cort 
6512106ea47SThomas Cort static int
compareunits(struct unittype * first,struct unittype * second)6522106ea47SThomas Cort compareunits(struct unittype * first, struct unittype * second)
6532106ea47SThomas Cort {
6542106ea47SThomas Cort 	return
6552106ea47SThomas Cort 	compareproducts(first->numerator, second->numerator) ||
6562106ea47SThomas Cort 	compareproducts(first->denominator, second->denominator);
6572106ea47SThomas Cort }
6582106ea47SThomas Cort 
6592106ea47SThomas Cort static int
compareunitsreciprocal(struct unittype * first,struct unittype * second)6602106ea47SThomas Cort compareunitsreciprocal(struct unittype * first, struct unittype * second)
6612106ea47SThomas Cort {
6622106ea47SThomas Cort 	return
6632106ea47SThomas Cort 	compareproducts(first->numerator, second->denominator) ||
6642106ea47SThomas Cort 	compareproducts(first->denominator, second->numerator);
6652106ea47SThomas Cort }
6662106ea47SThomas Cort 
6672106ea47SThomas Cort 
6682106ea47SThomas Cort static int
completereduce(struct unittype * unit)6692106ea47SThomas Cort completereduce(struct unittype * unit)
6702106ea47SThomas Cort {
6712106ea47SThomas Cort 	if (reduceunit(unit))
6722106ea47SThomas Cort 		return 1;
6732106ea47SThomas Cort 	sortunit(unit);
6742106ea47SThomas Cort 	cancelunit(unit);
6752106ea47SThomas Cort 	return 0;
6762106ea47SThomas Cort }
6772106ea47SThomas Cort 
6782106ea47SThomas Cort 
6792106ea47SThomas Cort static void
showanswer(struct unittype * have,struct unittype * want)6802106ea47SThomas Cort showanswer(struct unittype * have, struct unittype * want)
6812106ea47SThomas Cort {
6822106ea47SThomas Cort 	if (compareunits(have, want)) {
6832106ea47SThomas Cort 		if (compareunitsreciprocal(have, want)) {
6842106ea47SThomas Cort 			printf("conformability error\n");
6852106ea47SThomas Cort 			showunit(have);
6862106ea47SThomas Cort 			showunit(want);
6872106ea47SThomas Cort 		} else {
6882106ea47SThomas Cort 			printf("\treciprocal conversion\n");
6892106ea47SThomas Cort 			printf("\t* %.*g\n\t/ %.*g\n",
6902106ea47SThomas Cort 			    precision, 1 / (have->factor * want->factor),
6912106ea47SThomas Cort 			    precision, want->factor * have->factor);
6922106ea47SThomas Cort 		}
6932106ea47SThomas Cort 	}
6942106ea47SThomas Cort 	else
6952106ea47SThomas Cort 		printf("\t* %.*g\n\t/ %.*g\n",
6962106ea47SThomas Cort 		    precision, have->factor / want->factor,
6972106ea47SThomas Cort 		    precision, want->factor / have->factor);
6982106ea47SThomas Cort }
6992106ea47SThomas Cort 
7002106ea47SThomas Cort static int
listunits(int expand)7012106ea47SThomas Cort listunits(int expand)
7022106ea47SThomas Cort {
7032106ea47SThomas Cort 	struct unittype theunit;
7042106ea47SThomas Cort 	const char *thename;
7052106ea47SThomas Cort 	const char *thedefn;
7062106ea47SThomas Cort 	int errors = 0;
7072106ea47SThomas Cort 	int i;
7082106ea47SThomas Cort 	int printexpansion;
7092106ea47SThomas Cort 
7102106ea47SThomas Cort 	/*
7112106ea47SThomas Cort 	 * send error and warning messages to stdout,
7122106ea47SThomas Cort 	 * and make them look like comments.
7132106ea47SThomas Cort 	 */
7142106ea47SThomas Cort 	errprefix = "/ ";
7152106ea47SThomas Cort 
7162106ea47SThomas Cort #if 0 /* debug */
7172106ea47SThomas Cort 	printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n",
7182106ea47SThomas Cort 	    expand, precision, unitcount, prefixcount);
7192106ea47SThomas Cort #endif
7202106ea47SThomas Cort 
7212106ea47SThomas Cort 	/* 1. Dump all primitive units, e.g. "m !a!", "kg !b!", ... */
7222106ea47SThomas Cort 	printf("/ Primitive units\n");
7232106ea47SThomas Cort 	for (i = 0; i < unitcount; i++) {
7242106ea47SThomas Cort 		thename = unittable[i].uname;
7252106ea47SThomas Cort 		thedefn = unittable[i].uval;
7262106ea47SThomas Cort 		if (thedefn[0] == PRIMITIVECHAR) {
7272106ea47SThomas Cort 			printf("%s\t%s\n", thename, thedefn);
7282106ea47SThomas Cort 		}
7292106ea47SThomas Cort 	}
7302106ea47SThomas Cort 
7312106ea47SThomas Cort 	/* 2. Dump all prefixes, e.g. "yotta- 1e24", "zetta- 1e21", ... */
7322106ea47SThomas Cort 	printf("/ Prefixes\n");
7332106ea47SThomas Cort 	for (i = 0; i < prefixcount; i++) {
7342106ea47SThomas Cort 		printexpansion = expand;
7352106ea47SThomas Cort 		thename = prefixtable[i].prefixname;
7362106ea47SThomas Cort 		thedefn = prefixtable[i].prefixval;
7372106ea47SThomas Cort 		if (expand) {
7382106ea47SThomas Cort 			/*
7392106ea47SThomas Cort 			 * prefix names are sometimes identical to unit
7402106ea47SThomas Cort 			 * names, so we have to expand thedefn instead of
7412106ea47SThomas Cort 			 * expanding thename.
7422106ea47SThomas Cort 			 */
7432106ea47SThomas Cort 			initializeunit(&theunit);
7442106ea47SThomas Cort 			if (addunit(&theunit, thedefn, 0) != 0
7452106ea47SThomas Cort 			    || completereduce(&theunit) != 0) {
7462106ea47SThomas Cort 				errors++;
7472106ea47SThomas Cort 				printexpansion = 0;
7482106ea47SThomas Cort 				mywarnx("Error in prefix '%s-'", thename);
7492106ea47SThomas Cort 			}
7502106ea47SThomas Cort 		}
7512106ea47SThomas Cort 		if (printexpansion) {
7522106ea47SThomas Cort 			printf("%s-", thename);
7532106ea47SThomas Cort 			showunit(&theunit);
7542106ea47SThomas Cort 		} else
7552106ea47SThomas Cort 			printf("%s-\t%s\n", thename, thedefn);
7562106ea47SThomas Cort 	}
7572106ea47SThomas Cort 
7582106ea47SThomas Cort 	/* 3. Dump all other units. */
7592106ea47SThomas Cort 	printf("/ Other units\n");
7602106ea47SThomas Cort 	for (i = 0; i < unitcount; i++) {
7612106ea47SThomas Cort 		printexpansion = expand;
7622106ea47SThomas Cort 		thename = unittable[i].uname;
7632106ea47SThomas Cort 		thedefn = unittable[i].uval;
7642106ea47SThomas Cort 		if (thedefn[0] == PRIMITIVECHAR)
7652106ea47SThomas Cort 			continue;
7662106ea47SThomas Cort 		if (expand) {
7672106ea47SThomas Cort 			/*
7682106ea47SThomas Cort 			 * expand thename, not thedefn, so that
7692106ea47SThomas Cort 			 * we can catch errors in the name itself.
7702106ea47SThomas Cort 			 * e.g. a name that contains a hyphen
7712106ea47SThomas Cort 			 * will be interpreted as multiplication.
7722106ea47SThomas Cort 			 */
7732106ea47SThomas Cort 			initializeunit(&theunit);
7742106ea47SThomas Cort 			if (addunit(&theunit, thename, 0) != 0
7752106ea47SThomas Cort 			    || completereduce(&theunit) != 0) {
7762106ea47SThomas Cort 				errors++;
7772106ea47SThomas Cort 				printexpansion = 0;
7782106ea47SThomas Cort 				mywarnx("Error in unit '%s'", thename);
7792106ea47SThomas Cort 			}
7802106ea47SThomas Cort 		}
7812106ea47SThomas Cort 		if (printexpansion) {
7822106ea47SThomas Cort 			printf("%s", thename);
7832106ea47SThomas Cort 			showunit(&theunit);
7842106ea47SThomas Cort 		} else
7852106ea47SThomas Cort 			printf("%s\t%s\n", thename, thedefn);
7862106ea47SThomas Cort 	}
7872106ea47SThomas Cort 
7882106ea47SThomas Cort 	if (errors)
7892106ea47SThomas Cort 		mywarnx("Definitions with errors: %d", errors);
7902106ea47SThomas Cort 	return (errors ? 1 : 0);
7912106ea47SThomas Cort }
7922106ea47SThomas Cort 
7932106ea47SThomas Cort static void
usage(void)7942106ea47SThomas Cort usage(void)
7952106ea47SThomas Cort {
7962106ea47SThomas Cort 	fprintf(stderr,
7972106ea47SThomas Cort 	    "\nunits [-Llqv] [-f filename] [[count] from-unit to-unit]\n");
7982106ea47SThomas Cort 	fprintf(stderr, "\n    -f specify units file\n");
7992106ea47SThomas Cort 	fprintf(stderr, "    -L list units in standardized base units\n");
8002106ea47SThomas Cort 	fprintf(stderr, "    -l list units\n");
8012106ea47SThomas Cort 	fprintf(stderr, "    -q suppress prompting (quiet)\n");
8022106ea47SThomas Cort 	fprintf(stderr, "    -v print version number\n");
8032106ea47SThomas Cort 	exit(3);
8042106ea47SThomas Cort }
8052106ea47SThomas Cort 
8062106ea47SThomas Cort int
main(int argc,char ** argv)8072106ea47SThomas Cort main(int argc, char **argv)
8082106ea47SThomas Cort {
8092106ea47SThomas Cort 
8102106ea47SThomas Cort 	struct unittype have, want;
8112106ea47SThomas Cort 	char havestr[81], wantstr[81];
8122106ea47SThomas Cort 	int optchar;
8132106ea47SThomas Cort 	const char *userfile = 0;
8142106ea47SThomas Cort 	int list = 0, listexpand = 0;
8152106ea47SThomas Cort 	int quiet = 0;
8162106ea47SThomas Cort 
8172106ea47SThomas Cort 	while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) {
8182106ea47SThomas Cort 		switch (optchar) {
8192106ea47SThomas Cort 		case 'l':
8202106ea47SThomas Cort 			list = 1;
8212106ea47SThomas Cort 			break;
8222106ea47SThomas Cort 		case 'L':
8232106ea47SThomas Cort 			list = 1;
8242106ea47SThomas Cort 			listexpand = 1;
8252106ea47SThomas Cort 			precision = DBL_DIG;
8262106ea47SThomas Cort 			break;
8272106ea47SThomas Cort 		case 'f':
8282106ea47SThomas Cort 			userfile = optarg;
8292106ea47SThomas Cort 			break;
8302106ea47SThomas Cort 		case 'q':
8312106ea47SThomas Cort 			quiet = 1;
8322106ea47SThomas Cort 			break;
8332106ea47SThomas Cort 		case 'v':
8342106ea47SThomas Cort 			fprintf(stderr, "\n  units version %s  Copyright (c) 1993 by Adrian Mariano\n",
8352106ea47SThomas Cort 			    VERSION);
8362106ea47SThomas Cort 			fprintf(stderr, "                    This program may be freely distributed\n");
8372106ea47SThomas Cort 			usage();
8382106ea47SThomas Cort 		default:
8392106ea47SThomas Cort 			usage();
8402106ea47SThomas Cort 			break;
8412106ea47SThomas Cort 		}
8422106ea47SThomas Cort 	}
8432106ea47SThomas Cort 
8442106ea47SThomas Cort 	argc -= optind;
8452106ea47SThomas Cort 	argv += optind;
8462106ea47SThomas Cort 
8472106ea47SThomas Cort 	if ((argc != 3 && argc != 2 && argc != 0)
8482106ea47SThomas Cort 	    || (list && argc != 0))
8492106ea47SThomas Cort 		usage();
8502106ea47SThomas Cort 
8512106ea47SThomas Cort 	if (list)
8522106ea47SThomas Cort 		errprefix = "/ ";	/* set this before reading the file */
8532106ea47SThomas Cort 
8542106ea47SThomas Cort 	readunits(userfile);
8552106ea47SThomas Cort 
8562106ea47SThomas Cort 	if (list)
8572106ea47SThomas Cort 		return listunits(listexpand);
8582106ea47SThomas Cort 
8592106ea47SThomas Cort 	if (argc == 3) {
8602106ea47SThomas Cort 		strlcpy(havestr, argv[0], sizeof(havestr));
8612106ea47SThomas Cort 		strlcat(havestr, " ", sizeof(havestr));
8622106ea47SThomas Cort 		strlcat(havestr, argv[1], sizeof(havestr));
8632106ea47SThomas Cort 		argc--;
8642106ea47SThomas Cort 		argv++;
8652106ea47SThomas Cort 		argv[0] = havestr;
8662106ea47SThomas Cort 	}
8672106ea47SThomas Cort 
8682106ea47SThomas Cort 	if (argc == 2) {
8692106ea47SThomas Cort 		strlcpy(havestr, argv[0], sizeof(havestr));
8702106ea47SThomas Cort 		strlcpy(wantstr, argv[1], sizeof(wantstr));
8712106ea47SThomas Cort 		initializeunit(&have);
8722106ea47SThomas Cort 		addunit(&have, havestr, 0);
8732106ea47SThomas Cort 		completereduce(&have);
8742106ea47SThomas Cort 		initializeunit(&want);
8752106ea47SThomas Cort 		addunit(&want, wantstr, 0);
8762106ea47SThomas Cort 		completereduce(&want);
8772106ea47SThomas Cort 		showanswer(&have, &want);
8782106ea47SThomas Cort 	}
8792106ea47SThomas Cort 	else {
8802106ea47SThomas Cort 		if (!quiet)
8812106ea47SThomas Cort 			printf("%d units, %d prefixes\n\n", unitcount,
8822106ea47SThomas Cort 			    prefixcount);
8832106ea47SThomas Cort 		for (;;) {
8842106ea47SThomas Cort 			do {
8852106ea47SThomas Cort 				initializeunit(&have);
8862106ea47SThomas Cort 				if (!quiet)
8872106ea47SThomas Cort 					printf("You have: ");
8882106ea47SThomas Cort 				if (!fgets(havestr, 80, stdin)) {
8892106ea47SThomas Cort 					if (!quiet)
8902106ea47SThomas Cort 						putchar('\n');
8912106ea47SThomas Cort 					exit(0);
8922106ea47SThomas Cort 				}
8932106ea47SThomas Cort 			} while (addunit(&have, havestr, 0) ||
8942106ea47SThomas Cort 			    completereduce(&have));
8952106ea47SThomas Cort 			do {
8962106ea47SThomas Cort 				initializeunit(&want);
8972106ea47SThomas Cort 				if (!quiet)
8982106ea47SThomas Cort 					printf("You want: ");
8992106ea47SThomas Cort 				if (!fgets(wantstr, 80, stdin)) {
9002106ea47SThomas Cort 					if (!quiet)
9012106ea47SThomas Cort 						putchar('\n');
9022106ea47SThomas Cort 					exit(0);
9032106ea47SThomas Cort 				}
9042106ea47SThomas Cort 			} while (addunit(&want, wantstr, 0) ||
9052106ea47SThomas Cort 			    completereduce(&want));
9062106ea47SThomas Cort 			showanswer(&have, &want);
9072106ea47SThomas Cort 		}
9082106ea47SThomas Cort 	}
9092106ea47SThomas Cort 	return (0);
9102106ea47SThomas Cort }
911