Home | History | Annotate | Line # | Download | only in units
units.c revision 1.22
      1 /*	$NetBSD: units.c,v 1.22 2013/01/01 12:45:06 apb Exp $	*/
      2 
      3 /*
      4  * units.c   Copyright (c) 1993 by Adrian Mariano (adrian (at) cam.cornell.edu)
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. The name of the author may not be used to endorse or promote products
     12  *    derived from this software without specific prior written permission.
     13  * Disclaimer:  This software is provided by the author "as is".  The author
     14  * shall not be liable for any damages caused in any way by this software.
     15  *
     16  * I would appreciate (though I do not require) receiving a copy of any
     17  * improvements you might make to this program.
     18  */
     19 
     20 #include <ctype.h>
     21 #include <err.h>
     22 #include <float.h>
     23 #include <stdio.h>
     24 #include <string.h>
     25 #include <stdlib.h>
     26 #include <unistd.h>
     27 
     28 #include "pathnames.h"
     29 
     30 #define VERSION "1.0"
     31 
     32 #ifndef UNITSFILE
     33 #define UNITSFILE _PATH_UNITSLIB
     34 #endif
     35 
     36 #define MAXUNITS 1000
     37 #define MAXPREFIXES 50
     38 
     39 #define MAXSUBUNITS 500
     40 
     41 #define PRIMITIVECHAR '!'
     42 
     43 static int precision = 8;		/* for printf with "%.*g" format */
     44 
     45 static const char *errprefix = NULL;	/* if not NULL, then prepend this
     46 					 * to error messages and send them to
     47 					 * stdout instead of stderr.
     48 					 */
     49 
     50 static const char *powerstring = "^";
     51 
     52 static struct {
     53 	const char *uname;
     54 	const char *uval;
     55 }      unittable[MAXUNITS];
     56 
     57 struct unittype {
     58 	const char *numerator[MAXSUBUNITS];
     59 	const char *denominator[MAXSUBUNITS];
     60 	double factor;
     61 };
     62 
     63 struct {
     64 	const char *prefixname;
     65 	const char *prefixval;
     66 }      prefixtable[MAXPREFIXES];
     67 
     68 
     69 static const char *NULLUNIT = "";
     70 
     71 static int unitcount;
     72 static int prefixcount;
     73 
     74 
     75 static int	addsubunit(const char *[], const char *);
     76 static int	addunit(struct unittype *, const char *, int);
     77 static void	cancelunit(struct unittype *);
     78 static int	compare(const void *, const void *);
     79 static int	compareproducts(const char **, const char **);
     80 static int	compareunits(struct unittype *, struct unittype *);
     81 static int	compareunitsreciprocal(struct unittype *, struct unittype *);
     82 static int	completereduce(struct unittype *);
     83 static void	initializeunit(struct unittype *);
     84 static void	readerror(int);
     85 static void	readunits(const char *);
     86 static int	reduceproduct(struct unittype *, int);
     87 static int	reduceunit(struct unittype *);
     88 static void	showanswer(struct unittype *, struct unittype *);
     89 static void	showunit(struct unittype *);
     90 static void	sortunit(struct unittype *);
     91 __dead static void	usage(void);
     92 static void	zeroerror(void);
     93 static char   *dupstr(const char *);
     94 static const char *lookupunit(const char *);
     95 
     96 static char *
     97 dupstr(const char *str)
     98 {
     99 	char *ret;
    100 
    101 	ret = strdup(str);
    102 	if (!ret)
    103 		err(3, "Memory allocation error");
    104 	return (ret);
    105 }
    106 
    107 
    108 static void
    109 mywarnx(const char *fmt, ...)
    110 {
    111 	va_list args;
    112 
    113 	va_start(args, fmt);
    114 	if (errprefix) {
    115 		/* warn to stdout, with errprefix prepended */
    116 		printf("%s", errprefix);
    117 		vprintf(fmt, args);
    118 		printf("%s", "\n");
    119 	} else {
    120 		/* warn to stderr */
    121 		vwarnx(fmt, args);
    122 	}
    123 	va_end(args);
    124 }
    125 
    126 static void
    127 readerror(int linenum)
    128 {
    129 	mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum);
    130 }
    131 
    132 
    133 static void
    134 readunits(const char *userfile)
    135 {
    136 	FILE *unitfile;
    137 	char line[80], *lineptr;
    138 	int len, linenum, i, isdup;
    139 
    140 	unitcount = 0;
    141 	linenum = 0;
    142 
    143 	if (userfile) {
    144 		unitfile = fopen(userfile, "rt");
    145 		if (!unitfile)
    146 			err(1, "Unable to open units file '%s'", userfile);
    147 	}
    148 	else {
    149 		unitfile = fopen(UNITSFILE, "rt");
    150 		if (!unitfile) {
    151 			char *direc, *env;
    152 			char filename[1000];
    153 			char separator[2];
    154 
    155 			env = getenv("PATH");
    156 			if (env) {
    157 				if (strchr(env, ';'))
    158 					strlcpy(separator, ";",
    159 					    sizeof(separator));
    160 				else
    161 					strlcpy(separator, ":",
    162 					    sizeof(separator));
    163 				direc = strtok(env, separator);
    164 				while (direc) {
    165 					strlcpy(filename, "", sizeof(filename));
    166 					strlcat(filename, direc,
    167 					    sizeof(filename));
    168 					strlcat(filename, "/",
    169 					    sizeof(filename));
    170 					strlcat(filename, UNITSFILE,
    171 					    sizeof(filename));
    172 					unitfile = fopen(filename, "rt");
    173 					if (unitfile)
    174 						break;
    175 					direc = strtok(NULL, separator);
    176 				}
    177 			}
    178 			if (!unitfile)
    179 				errx(1, "Can't find units file '%s'",
    180 				    UNITSFILE);
    181 		}
    182 	}
    183 	while (!feof(unitfile)) {
    184 		if (!fgets(line, 79, unitfile))
    185 			break;
    186 		linenum++;
    187 		lineptr = line;
    188 		if (*lineptr == '/')
    189 			continue;
    190 		lineptr += strspn(lineptr, " \n\t");
    191 		len = strcspn(lineptr, " \n\t");
    192 		lineptr[len] = 0;
    193 		if (!strlen(lineptr))
    194 			continue;
    195 		if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
    196 			if (prefixcount == MAXPREFIXES) {
    197 				mywarnx(
    198 			"Memory for prefixes exceeded in line %d",
    199 					linenum);
    200 				continue;
    201 			}
    202 			lineptr[strlen(lineptr) - 1] = 0;
    203 			for (isdup = 0, i = 0; i < prefixcount; i++) {
    204 				if (!strcmp(prefixtable[i].prefixname,
    205 				    lineptr)) {
    206 					isdup = 1;
    207 					break;
    208 				}
    209 			}
    210 			if (isdup) {
    211 				mywarnx(
    212 			"Redefinition of prefix '%s' on line %d ignored",
    213 				    lineptr, linenum);
    214 				continue;
    215 			}
    216 			prefixtable[prefixcount].prefixname = dupstr(lineptr);
    217 			lineptr += len + 1;
    218 			if (!strlen(lineptr)) {
    219 				readerror(linenum);
    220 				continue;
    221 			}
    222 			lineptr += strspn(lineptr, " \n\t");
    223 			len = strcspn(lineptr, "\n\t");
    224 			lineptr[len] = 0;
    225 			prefixtable[prefixcount++].prefixval = dupstr(lineptr);
    226 		}
    227 		else {		/* it's not a prefix */
    228 			if (unitcount == MAXUNITS) {
    229 				mywarnx("Memory for units exceeded in line %d",
    230 				    linenum);
    231 				continue;
    232 			}
    233 			for (isdup = 0, i = 0; i < unitcount; i++) {
    234 				if (!strcmp(unittable[i].uname, lineptr)) {
    235 					isdup = 1;
    236 					break;
    237 				}
    238 			}
    239 			if (isdup) {
    240 				mywarnx(
    241 				"Redefinition of unit '%s' on line %d ignored",
    242 				    lineptr, linenum);
    243 				continue;
    244 			}
    245 			unittable[unitcount].uname = dupstr(lineptr);
    246 			lineptr += len + 1;
    247 			lineptr += strspn(lineptr, " \n\t");
    248 			if (!strlen(lineptr)) {
    249 				readerror(linenum);
    250 				continue;
    251 			}
    252 			len = strcspn(lineptr, "\n\t");
    253 			lineptr[len] = 0;
    254 			unittable[unitcount++].uval = dupstr(lineptr);
    255 		}
    256 	}
    257 	fclose(unitfile);
    258 }
    259 
    260 static void
    261 initializeunit(struct unittype * theunit)
    262 {
    263 	theunit->factor = 1.0;
    264 	theunit->numerator[0] = theunit->denominator[0] = NULL;
    265 }
    266 
    267 static int
    268 addsubunit(const char *product[], const char *toadd)
    269 {
    270 	const char **ptr;
    271 
    272 	for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
    273 	if (ptr >= product + MAXSUBUNITS) {
    274 		mywarnx("Memory overflow in unit reduction");
    275 		return 1;
    276 	}
    277 	if (!*ptr)
    278 		*(ptr + 1) = 0;
    279 	*ptr = dupstr(toadd);
    280 	return 0;
    281 }
    282 
    283 static void
    284 showunit(struct unittype * theunit)
    285 {
    286 	const char **ptr;
    287 	int printedslash;
    288 	int counter = 1;
    289 
    290 	printf("\t%.*g", precision, theunit->factor);
    291 	for (ptr = theunit->numerator; *ptr; ptr++) {
    292 		if (ptr > theunit->numerator && **ptr &&
    293 		    !strcmp(*ptr, *(ptr - 1)))
    294 			counter++;
    295 		else {
    296 			if (counter > 1)
    297 				printf("%s%d", powerstring, counter);
    298 			if (**ptr)
    299 				printf(" %s", *ptr);
    300 			counter = 1;
    301 		}
    302 	}
    303 	if (counter > 1)
    304 		printf("%s%d", powerstring, counter);
    305 	counter = 1;
    306 	printedslash = 0;
    307 	for (ptr = theunit->denominator; *ptr; ptr++) {
    308 		if (ptr > theunit->denominator && **ptr &&
    309 		    !strcmp(*ptr, *(ptr - 1)))
    310 			counter++;
    311 		else {
    312 			if (counter > 1)
    313 				printf("%s%d", powerstring, counter);
    314 			if (**ptr) {
    315 				if (!printedslash)
    316 					printf(" /");
    317 				printedslash = 1;
    318 				printf(" %s", *ptr);
    319 			}
    320 			counter = 1;
    321 		}
    322 	}
    323 	if (counter > 1)
    324 		printf("%s%d", powerstring, counter);
    325 	printf("\n");
    326 }
    327 
    328 static void
    329 zeroerror(void)
    330 {
    331 	mywarnx("Unit reduces to zero");
    332 }
    333 
    334 /*
    335    Adds the specified string to the unit.
    336    Flip is 0 for adding normally, 1 for adding reciprocal.
    337 
    338    Returns 0 for successful addition, nonzero on error.
    339 */
    340 
    341 static int
    342 addunit(struct unittype * theunit, const char *toadd, int flip)
    343 {
    344 	char *scratch, *savescr;
    345 	char *item;
    346 	char *divider, *slash;
    347 	int doingtop;
    348 
    349 	savescr = scratch = dupstr(toadd);
    350 	for (slash = scratch + 1; *slash; slash++)
    351 		if (*slash == '-' &&
    352 		    (tolower((unsigned char)*(slash - 1)) != 'e' ||
    353 		    !strchr(".0123456789", *(slash + 1))))
    354 			*slash = ' ';
    355 	slash = strchr(scratch, '/');
    356 	if (slash)
    357 		*slash = 0;
    358 	doingtop = 1;
    359 	do {
    360 		item = strtok(scratch, " *\t\n/");
    361 		while (item) {
    362 			if (strchr("0123456789.", *item)) {
    363 				/* item starts with a number */
    364 				char *endptr;
    365 				double num;
    366 
    367 				divider = strchr(item, '|');
    368 				if (divider) {
    369 					*divider = 0;
    370 					num = strtod(item, &endptr);
    371 					if (!num) {
    372 						zeroerror();
    373 						return 1;
    374 					}
    375 					if (endptr != divider) {
    376 						/* "6foo|2" is an error */
    377 						mywarnx("Junk before '|'");
    378 						return 1;
    379 					}
    380 					if (doingtop ^ flip)
    381 						theunit->factor *= num;
    382 					else
    383 						theunit->factor /= num;
    384 					num = strtod(divider + 1, &endptr);
    385 					if (!num) {
    386 						zeroerror();
    387 						return 1;
    388 					}
    389 					if (doingtop ^ flip)
    390 						theunit->factor /= num;
    391 					else
    392 						theunit->factor *= num;
    393 					if (*endptr) {
    394 						/* "6|2foo" is like "6|2 foo" */
    395 						item = endptr;
    396 						continue;
    397 					}
    398 				}
    399 				else {
    400 					num = strtod(item, &endptr);
    401 					if (!num) {
    402 						zeroerror();
    403 						return 1;
    404 					}
    405 					if (doingtop ^ flip)
    406 						theunit->factor *= num;
    407 					else
    408 						theunit->factor /= num;
    409 					if (*endptr) {
    410 						/* "3foo" is like "3 foo" */
    411 						item = endptr;
    412 						continue;
    413 					}
    414 				}
    415 			}
    416 			else {	/* item is not a number */
    417 				int repeat = 1;
    418 
    419 				if (strchr("23456789",
    420 				    item[strlen(item) - 1])) {
    421 					repeat = item[strlen(item) - 1] - '0';
    422 					item[strlen(item) - 1] = 0;
    423 				}
    424 				for (; repeat; repeat--)
    425 					if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
    426 						return 1;
    427 			}
    428 			item = strtok(NULL, " *\t/\n");
    429 		}
    430 		doingtop--;
    431 		if (slash) {
    432 			scratch = slash + 1;
    433 		}
    434 		else
    435 			doingtop--;
    436 	} while (doingtop >= 0);
    437 	free(savescr);
    438 	return 0;
    439 }
    440 
    441 static int
    442 compare(const void *item1, const void *item2)
    443 {
    444 	return strcmp(*(const char * const *) item1,
    445 		      *(const char * const *) item2);
    446 }
    447 
    448 static void
    449 sortunit(struct unittype * theunit)
    450 {
    451 	const char **ptr;
    452 	int count;
    453 
    454 	for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
    455 	qsort(theunit->numerator, count, sizeof(char *), compare);
    456 	for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
    457 	qsort(theunit->denominator, count, sizeof(char *), compare);
    458 }
    459 
    460 static void
    461 cancelunit(struct unittype * theunit)
    462 {
    463 	const char **den, **num;
    464 	int comp;
    465 
    466 	den = theunit->denominator;
    467 	num = theunit->numerator;
    468 
    469 	while (*num && *den) {
    470 		comp = strcmp(*den, *num);
    471 		if (!comp) {
    472 /*      if (*den!=NULLUNIT) free(*den);
    473       if (*num!=NULLUNIT) free(*num);*/
    474 			*den++ = NULLUNIT;
    475 			*num++ = NULLUNIT;
    476 		}
    477 		else if (comp < 0)
    478 			den++;
    479 		else
    480 			num++;
    481 	}
    482 }
    483 
    484 
    485 
    486 
    487 /*
    488    Looks up the definition for the specified unit.
    489    Returns a pointer to the definition or a null pointer
    490    if the specified unit does not appear in the units table.
    491 */
    492 
    493 static char buffer[100];	/* buffer for lookupunit answers with
    494 				   prefixes */
    495 
    496 static const char *
    497 lookupunit(const char *unit)
    498 {
    499 	int i;
    500 	char *copy;
    501 
    502 	for (i = 0; i < unitcount; i++) {
    503 		if (!strcmp(unittable[i].uname, unit))
    504 			return unittable[i].uval;
    505 	}
    506 
    507 	if (unit[strlen(unit) - 1] == '^') {
    508 		copy = dupstr(unit);
    509 		copy[strlen(copy) - 1] = 0;
    510 		for (i = 0; i < unitcount; i++) {
    511 			if (!strcmp(unittable[i].uname, copy)) {
    512 				strlcpy(buffer, copy, sizeof(buffer));
    513 				free(copy);
    514 				return buffer;
    515 			}
    516 		}
    517 		free(copy);
    518 	}
    519 	if (unit[strlen(unit) - 1] == 's') {
    520 		copy = dupstr(unit);
    521 		copy[strlen(copy) - 1] = 0;
    522 		for (i = 0; i < unitcount; i++) {
    523 			if (!strcmp(unittable[i].uname, copy)) {
    524 				strlcpy(buffer, copy, sizeof(buffer));
    525 				free(copy);
    526 				return buffer;
    527 			}
    528 		}
    529 		if (copy[strlen(copy) - 1] == 'e') {
    530 			copy[strlen(copy) - 1] = 0;
    531 			for (i = 0; i < unitcount; i++) {
    532 				if (!strcmp(unittable[i].uname, copy)) {
    533 					strlcpy(buffer, copy, sizeof(buffer));
    534 					free(copy);
    535 					return buffer;
    536 				}
    537 			}
    538 		}
    539 		free(copy);
    540 	}
    541 	for (i = 0; i < prefixcount; i++) {
    542 		if (!strncmp(prefixtable[i].prefixname, unit,
    543 			strlen(prefixtable[i].prefixname))) {
    544 			unit += strlen(prefixtable[i].prefixname);
    545 			if (!strlen(unit) || lookupunit(unit)) {
    546 				strlcpy(buffer, prefixtable[i].prefixval,
    547 				    sizeof(buffer));
    548 				strlcat(buffer, " ", sizeof(buffer));
    549 				strlcat(buffer, unit, sizeof(buffer));
    550 				return buffer;
    551 			}
    552 		}
    553 	}
    554 	return 0;
    555 }
    556 
    557 
    558 
    559 /*
    560    reduces a product of symbolic units to primitive units.
    561    The three low bits are used to return flags:
    562 
    563      bit 0 (1) set on if reductions were performed without error.
    564      bit 1 (2) set on if no reductions are performed.
    565      bit 2 (4) set on if an unknown unit is discovered.
    566 */
    567 
    568 
    569 #define ERROR 4
    570 
    571 static int
    572 reduceproduct(struct unittype * theunit, int flip)
    573 {
    574 
    575 	const char *toadd;
    576 	const char **product;
    577 	int didsomething = 2;
    578 
    579 	if (flip)
    580 		product = theunit->denominator;
    581 	else
    582 		product = theunit->numerator;
    583 
    584 	for (; *product; product++) {
    585 
    586 		for (;;) {
    587 			if (!strlen(*product))
    588 				break;
    589 			toadd = lookupunit(*product);
    590 			if (!toadd) {
    591 				mywarnx("Unknown unit '%s'", *product);
    592 				return ERROR;
    593 			}
    594 			if (strchr(toadd, PRIMITIVECHAR))
    595 				break;
    596 			didsomething = 1;
    597 			if (*product != NULLUNIT) {
    598 				free(__UNCONST(*product));
    599 				*product = NULLUNIT;
    600 			}
    601 			if (addunit(theunit, toadd, flip))
    602 				return ERROR;
    603 		}
    604 	}
    605 	return didsomething;
    606 }
    607 
    608 
    609 /*
    610    Reduces numerator and denominator of the specified unit.
    611    Returns 0 on success, or 1 on unknown unit error.
    612 */
    613 
    614 static int
    615 reduceunit(struct unittype * theunit)
    616 {
    617 	int ret;
    618 
    619 	ret = 1;
    620 	while (ret & 1) {
    621 		ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
    622 		if (ret & 4)
    623 			return 1;
    624 	}
    625 	return 0;
    626 }
    627 
    628 static int
    629 compareproducts(const char **one, const char **two)
    630 {
    631 	while (*one || *two) {
    632 		if (!*one && *two != NULLUNIT)
    633 			return 1;
    634 		if (!*two && *one != NULLUNIT)
    635 			return 1;
    636 		if (*one == NULLUNIT)
    637 			one++;
    638 		else if (*two == NULLUNIT)
    639 			two++;
    640 		else if (*one && *two && strcmp(*one, *two))
    641 			return 1;
    642 		else
    643 			one++, two++;
    644 	}
    645 	return 0;
    646 }
    647 
    648 
    649 /* Return zero if units are compatible, nonzero otherwise */
    650 
    651 static int
    652 compareunits(struct unittype * first, struct unittype * second)
    653 {
    654 	return
    655 	compareproducts(first->numerator, second->numerator) ||
    656 	compareproducts(first->denominator, second->denominator);
    657 }
    658 
    659 static int
    660 compareunitsreciprocal(struct unittype * first, struct unittype * second)
    661 {
    662 	return
    663 	compareproducts(first->numerator, second->denominator) ||
    664 	compareproducts(first->denominator, second->numerator);
    665 }
    666 
    667 
    668 static int
    669 completereduce(struct unittype * unit)
    670 {
    671 	if (reduceunit(unit))
    672 		return 1;
    673 	sortunit(unit);
    674 	cancelunit(unit);
    675 	return 0;
    676 }
    677 
    678 
    679 static void
    680 showanswer(struct unittype * have, struct unittype * want)
    681 {
    682 	if (compareunits(have, want)) {
    683 		if (compareunitsreciprocal(have, want)) {
    684 			printf("conformability error\n");
    685 			showunit(have);
    686 			showunit(want);
    687 		} else {
    688 			printf("\treciprocal conversion\n");
    689 			printf("\t* %.*g\n\t/ %.*g\n",
    690 			    precision, 1 / (have->factor * want->factor),
    691 			    precision, want->factor * have->factor);
    692 		}
    693 	}
    694 	else
    695 		printf("\t* %.*g\n\t/ %.*g\n",
    696 		    precision, have->factor / want->factor,
    697 		    precision, want->factor / have->factor);
    698 }
    699 
    700 static int
    701 listunits(int expand)
    702 {
    703 	struct unittype theunit;
    704 	const char *thename;
    705 	const char *thedefn;
    706 	int errors = 0;
    707 	int i;
    708 	int printexpansion;
    709 
    710 	/*
    711 	 * send error and warning messages to stdout,
    712 	 * and make them look like comments.
    713 	 */
    714 	errprefix = "/ ";
    715 
    716 #if 0 /* debug */
    717 	printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n",
    718 	    expand, precision, unitcount, prefixcount);
    719 #endif
    720 
    721 	/* 1. Dump all primitive units, e.g. "m !a!", "kg !b!", ... */
    722 	printf("/ Primitive units\n");
    723 	for (i = 0; i < unitcount; i++) {
    724 		thename = unittable[i].uname;
    725 		thedefn = unittable[i].uval;
    726 		if (thedefn[0] == PRIMITIVECHAR) {
    727 			printf("%s\t%s\n", thename, thedefn);
    728 		}
    729 	}
    730 
    731 	/* 2. Dump all prefixes, e.g. "yotta- 1e24", "zetta- 1e21", ... */
    732 	printf("/ Prefixes\n");
    733 	for (i = 0; i < prefixcount; i++) {
    734 		printexpansion = expand;
    735 		thename = prefixtable[i].prefixname;
    736 		thedefn = prefixtable[i].prefixval;
    737 		if (expand) {
    738 			/*
    739 			 * prefix names are sometimes identical to unit
    740 			 * names, so we have to expand thedefn instead of
    741 			 * expanding thename.
    742 			 */
    743 			initializeunit(&theunit);
    744 			if (addunit(&theunit, thedefn, 0) != 0
    745 			    || completereduce(&theunit) != 0) {
    746 				errors++;
    747 				printexpansion = 0;
    748 				mywarnx("Error in prefix '%s-'", thename);
    749 			}
    750 		}
    751 		if (printexpansion) {
    752 			printf("%s-", thename);
    753 			showunit(&theunit);
    754 		} else
    755 			printf("%s-\t%s\n", thename, thedefn);
    756 	}
    757 
    758 	/* 3. Dump all other units. */
    759 	printf("/ Other units\n");
    760 	for (i = 0; i < unitcount; i++) {
    761 		printexpansion = expand;
    762 		thename = unittable[i].uname;
    763 		thedefn = unittable[i].uval;
    764 		if (thedefn[0] == PRIMITIVECHAR)
    765 			continue;
    766 		if (expand) {
    767 			/*
    768 			 * expand thename, not thedefn, so that
    769 			 * we can catch errors in the name itself.
    770 			 * e.g. a name that contains a hyphen
    771 			 * will be interpreted as multiplication.
    772 			 */
    773 			initializeunit(&theunit);
    774 			if (addunit(&theunit, thedefn/*XXX*/, 0) != 0
    775 			    || completereduce(&theunit) != 0) {
    776 				errors++;
    777 				printexpansion = 0;
    778 				mywarnx("Error in unit '%s'", thename);
    779 			}
    780 		}
    781 		if (printexpansion) {
    782 			printf("%s", thename);
    783 			showunit(&theunit);
    784 		} else
    785 			printf("%s\t%s\n", thename, thedefn);
    786 	}
    787 
    788 	if (errors)
    789 		mywarnx("Definitions with errors: %d", errors);
    790 	return (errors ? 1 : 0);
    791 }
    792 
    793 static void
    794 usage(void)
    795 {
    796 	fprintf(stderr,
    797 	    "\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
    798 	fprintf(stderr, "\n    -f specify units file\n");
    799 	fprintf(stderr, "    -q suppress prompting (quiet)\n");
    800 	fprintf(stderr, "    -v print version number\n");
    801 	exit(3);
    802 }
    803 
    804 int
    805 main(int argc, char **argv)
    806 {
    807 
    808 	struct unittype have, want;
    809 	char havestr[81], wantstr[81];
    810 	int optchar;
    811 	const char *userfile = 0;
    812 	int list = 0, listexpand = 0;
    813 	int quiet = 0;
    814 
    815 	while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) {
    816 		switch (optchar) {
    817 		case 'l':
    818 			list = 1;
    819 			break;
    820 		case 'L':
    821 			list = 1;
    822 			listexpand = 1;
    823 			precision = DBL_DIG;
    824 			break;
    825 		case 'f':
    826 			userfile = optarg;
    827 			break;
    828 		case 'q':
    829 			quiet = 1;
    830 			break;
    831 		case 'v':
    832 			fprintf(stderr, "\n  units version %s  Copyright (c) 1993 by Adrian Mariano\n",
    833 			    VERSION);
    834 			fprintf(stderr, "                    This program may be freely distributed\n");
    835 			usage();
    836 		default:
    837 			usage();
    838 			break;
    839 		}
    840 	}
    841 
    842 	argc -= optind;
    843 	argv += optind;
    844 
    845 	if ((argc != 3 && argc != 2 && argc != 0)
    846 	    || (list && argc != 0))
    847 		usage();
    848 
    849 	if (list)
    850 		errprefix = "/ ";	/* set this before reading the file */
    851 
    852 	readunits(userfile);
    853 
    854 	if (list)
    855 		return listunits(listexpand);
    856 
    857 	if (argc == 3) {
    858 		strlcpy(havestr, argv[0], sizeof(havestr));
    859 		strlcat(havestr, " ", sizeof(havestr));
    860 		strlcat(havestr, argv[1], sizeof(havestr));
    861 		argc--;
    862 		argv++;
    863 		argv[0] = havestr;
    864 	}
    865 
    866 	if (argc == 2) {
    867 		strlcpy(havestr, argv[0], sizeof(havestr));
    868 		strlcpy(wantstr, argv[1], sizeof(wantstr));
    869 		initializeunit(&have);
    870 		addunit(&have, havestr, 0);
    871 		completereduce(&have);
    872 		initializeunit(&want);
    873 		addunit(&want, wantstr, 0);
    874 		completereduce(&want);
    875 		showanswer(&have, &want);
    876 	}
    877 	else {
    878 		if (!quiet)
    879 			printf("%d units, %d prefixes\n\n", unitcount,
    880 			    prefixcount);
    881 		for (;;) {
    882 			do {
    883 				initializeunit(&have);
    884 				if (!quiet)
    885 					printf("You have: ");
    886 				if (!fgets(havestr, 80, stdin)) {
    887 					if (!quiet)
    888 						putchar('\n');
    889 					exit(0);
    890 				}
    891 			} while (addunit(&have, havestr, 0) ||
    892 			    completereduce(&have));
    893 			do {
    894 				initializeunit(&want);
    895 				if (!quiet)
    896 					printf("You want: ");
    897 				if (!fgets(wantstr, 80, stdin)) {
    898 					if (!quiet)
    899 						putchar('\n');
    900 					exit(0);
    901 				}
    902 			} while (addunit(&want, wantstr, 0) ||
    903 			    completereduce(&want));
    904 			showanswer(&have, &want);
    905 		}
    906 	}
    907 	return (0);
    908 }
    909