Importing usr.bin/units

No Minix-specific changes needed.

Change-Id: I2a3411bf78d5e31875d577dcd6df68f76da98137
This commit is contained in:
Thomas Cort 2013-10-23 19:24:43 -04:00
parent 9068050135
commit 2106ea4754
9 changed files with 1861 additions and 1 deletions

View file

@ -517,6 +517,7 @@
./usr/bin/uncompress minix-sys
./usr/bin/unexpand minix-sys
./usr/bin/uniq minix-sys
./usr/bin/units minix-sys
./usr/bin/unlzma minix-sys
./usr/bin/unstack minix-sys
./usr/bin/unxz minix-sys
@ -2076,6 +2077,7 @@
./usr/man/man1/uncompress.1 minix-sys
./usr/man/man1/unexpand.1 minix-sys
./usr/man/man1/uniq.1 minix-sys
./usr/man/man1/units.1 minix-sys
./usr/man/man1/unlzma.1 minix-sys
./usr/man/man1/unset.1 minix-sys
./usr/man/man1/unxz.1 minix-sys
@ -4636,6 +4638,7 @@
./usr/share/misc/texinfo.dtd minix-sys
./usr/share/misc/texinfo.tex minix-sys
./usr/share/misc/texinfo.xsl minix-sys
./usr/share/misc/units.lib minix-sys
./usr/share/misc/zipcodes minix-sys
./usr/share/mk minix-sys
./usr/share/mk/bsd.clang-analyze.mk minix-sys

View file

@ -204,6 +204,7 @@
2012/10/17 12:00:00,usr.bin/tr
2012/10/17 12:00:00,usr.bin/tsort
2010/10/06 07:59:18,usr.bin/uniq
2013/10/23 12:00:00,usr.bin/units
2013/10/14 12:00:00,usr.bin/unzip
2013/10/14 12:00:00,usr.bin/users
2012/10/17 12:00:00,usr.bin/wc

View file

@ -28,7 +28,7 @@ SUBDIR= \
tee tic tput \
tr tsort unexpand \
toproto \
uniq unzip users \
uniq units unzip users \
\
wc whatis who \
xargs xinstall yes

11
usr.bin/units/Makefile Normal file
View file

@ -0,0 +1,11 @@
# $NetBSD: Makefile,v 1.6 1999/02/13 02:54:56 lukem Exp $
.include <bsd.own.mk>
PROG= units
.if ${MKSHARE} != "no"
FILES= units.lib
FILESDIR=/usr/share/misc
.endif
.include <bsd.prog.mk>

18
usr.bin/units/README Normal file
View file

@ -0,0 +1,18 @@
# $NetBSD: README,v 1.2 1996/04/06 06:00:59 thorpej Exp $
This is a program which I wrote as a clone of the UNIX 'units'
command. I threw it together in a couple days, but it seems to work,
with some restrictions. I have tested it under DOS with Borland C and
Ultrix 4.2, and SunOS 4.1.
This program differs from the unix units program in the following
ways:
it can gracefully handle exponents larger than 9 in output
it uses 'e' to denote exponentiation in numbers
prefixes are listed in the units file
it tries both -s and -es plurals
it allows use of * for multiply and ^ for exponentiation in the input
the output format is somewhat different
Adrian Mariano (adrian@cam.cornell.edu or mariano@geom.umn.edu)

37
usr.bin/units/pathnames.h Normal file
View file

@ -0,0 +1,37 @@
/* $NetBSD: pathnames.h,v 1.6 2003/07/26 20:34:14 salo Exp $ */
/*
* Copyright (c) 1993 Christopher G. Demetriou
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed for the
* NetBSD Project. See http://www.NetBSD.org/ for
* information about NetBSD.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>>
*/
#define _PATH_UNITSLIB "/usr/share/misc/units.lib"

229
usr.bin/units/units.1 Normal file
View file

@ -0,0 +1,229 @@
.\" $NetBSD: units.1,v 1.21 2013/07/20 21:40:01 wiz Exp $
.Dd January 6, 2013
.Dt UNITS 1
.Os
.Sh NAME
.Nm units
.Nd conversion program
.Sh SYNOPSIS
.Nm
.Op Fl Llqv
.Op Fl f Ar filename
.Oo
.Op Ar count
.Ar from-unit to-unit
.Oc
.Sh DESCRIPTION
.Nm
converts quantities expression in various scales to
their equivalents in other scales.
.Nm
can only handle multiplicative scale changes.
It cannot convert Centigrade to Fahrenheit, for example.
.Pp
The following options and arguments are supported:
.Bl -tag -width "-fXfilenameX" -offset indent
.It Fl f Ar filename
Specifies the name of the units data file to load.
.It Fl l No or Fl L
List all unit definitions to the standard output,
instead of performing any conversions.
The result may include error messages and comments, beginning with
.Ql \&/ .
.Pp
With the
.Fl l
option, unit definitions will be listed in a format
almost identical to the the units data file that was loaded,
except that comments will be removed, spacing may be changed,
and lines may be re-ordered.
.Pp
With the
.Fl L
option, all unit definitions will be reduced to a form that
depends on only a few primitive units (such as
.Sy m , kg , sec ) .
.It Fl q
Suppresses prompting of the user for units and the display of statistics
about the number of units loaded.
.It Fl v
Prints the version number.
.It Oo Ar count Oc Ar from-unit Ar to-unit
Allows a single unit conversion to be done directly from the command
line.
No prompting will occur.
.Nm
will print out only the result of this single conversion.
Specifying
.Ar count
and
.Ar from-unit
as two separate arguments is equivalent to embedding both parts
inside a single
.Ar from-unit
argument, with the parts separated by a space.
.El
.Pp
.Nm
works interactively by prompting the user for input:
.Bd -literal
You have: meters
You want: feet
* 3.2808399
/ 0.3048
You have: cm^3
You want: gallons
* 0.00026417205
/ 3785.4118
.Ed
.Pp
Powers of units can be specified using the
.Dq \&^
character as shown in the example, or by simple concatenation:
.Dq cm3
is equivalent to
.Dq cm^3 .
Multiplication of units can be specified by using spaces, a dash or
an asterisk.
Division of units is indicated by the slash
.Pq Sq \&/ .
Note that multiplication has a higher precedence than division,
so
.Dq m/s/s
is the same as
.Dq m/s^2
or
.Dq "m/s s" .
If the user enters incompatible unit types, the
.Nm
program will print a message indicating that the units are not
conformable and it will display the reduced form for each unit:
.Bd -literal
You have: ergs/hour
You want: fathoms kg^2 / day
conformability error
2.7777778e-11 kg m^2 / sec^3
2.1166667e-05 kg^2 m / sec
.Ed
.Pp
The conversion information is read from a units data file.
The default
file includes definitions for most familiar units, abbreviations and
metric prefixes.
Some constants of nature included are:
.Bl -tag -width mercury -compact -offset indent
.It pi
ratio of circumference to diameter
.It c
speed of light
.It e
charge on an electron
.It g
acceleration of gravity
.It force
same as g
.It mole
Avogadro's number
.It water
pressure per unit height of water
.It mercury
pressure per unit height of mercury
.It au
astronomical unit
.El
.Pp
.Dq pound
is a unit of mass.
Compound names are run together
so
.Dq poundforce
is a unit of force.
British units that differ from their
US counterparts are prefixed with
.Dq br ,
and currency is prefixed with
its country name:
.Dq belgiumfranc ,
.Dq britainpound .
When searching for
a unit, if the specified string does not appear exactly as a unit
name, then the
.Nm
program will try to remove a trailing
.Dq s
or a trailing
.Dq es
and check again for a match.
.Pp
All of these definitions can be read in the standard units file, or you
can supply your own file.
A unit is specified on a single line by
giving its name and an equivalence.
One should be careful to define
new units in terms of old ones so that a reduction leads to the
primitive units which are marked with
.Sq \&!
characters.
.Nm
will not detect infinite loops that could be caused
by careless unit definitions.
.Pp
Prefixes are defined in the same way as standard units, but with
a trailing dash at the end of the prefix name.
.Sh FILES
.Bl -tag -width /usr/share/misc/units.lib -compact
.It Pa /usr/share/misc/units.lib
the standard units library
.El
.Sh AUTHORS
.An Adrian Mariano Aq Mt adrian@cam.cornell.edu
or
.Aq mariano@geom.umn.edu
.Sh CAVEATS
While
.Nm
can be used as a calculator for many unit-related computations,
caution is required: many computations require additional constant
factors deriving from the physics (or chemistry or whatever) of the
situation.
As these factors are dimensionless,
.Nm
cannot itself either provide them or warn the user when they have been
forgotten.
For example, one joule is one kilogram meter squared per second
squared, by definition; however, the kinetic energy of a one-kilogram
object moving at one meter per second is half a joule, not one joule,
because of a dimensionless factor that arises from integration.
.Pp
Also, some pairs of units that have the same dimensionality are
nonetheless used to measure different things and attempting to convert
between them may require additional fudge factors or be entirely
meaningless.
For example, torque and energy have the same dimensionality, but
attempting to convert torque in newton-meters to energy in joules is
nonsensical.
There is no practical way for
.Nm
to warn about these issues either.
.Sh BUGS
The effect of including a
.Sq \&/
in a prefix is surprising.
.Pp
Exponents entered by the user can be only one digit.
You can work around this by multiplying several terms.
.Pp
The user must use
.Sq \&|
to indicate division of numbers and
.Sq \&/
to indicate division of symbols.
This distinction should not be necessary.
.Pp
The program contains various arbitrary limits on the length
of the units converted and on the length of the data file.
.Pp
The program should use a hash table to store units so that
it doesn't take so long to load the units list and check
for duplication.

910
usr.bin/units/units.c Normal file
View file

@ -0,0 +1,910 @@
/* $NetBSD: units.c,v 1.24 2013/01/06 00:19:13 wiz Exp $ */
/*
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* Disclaimer: This software is provided by the author "as is". The author
* shall not be liable for any damages caused in any way by this software.
*
* I would appreciate (though I do not require) receiving a copy of any
* improvements you might make to this program.
*/
#include <ctype.h>
#include <err.h>
#include <float.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "pathnames.h"
#define VERSION "1.0"
#ifndef UNITSFILE
#define UNITSFILE _PATH_UNITSLIB
#endif
#define MAXUNITS 1000
#define MAXPREFIXES 50
#define MAXSUBUNITS 500
#define PRIMITIVECHAR '!'
static int precision = 8; /* for printf with "%.*g" format */
static const char *errprefix = NULL; /* if not NULL, then prepend this
* to error messages and send them to
* stdout instead of stderr.
*/
static const char *powerstring = "^";
static struct {
const char *uname;
const char *uval;
} unittable[MAXUNITS];
struct unittype {
const char *numerator[MAXSUBUNITS];
const char *denominator[MAXSUBUNITS];
double factor;
};
struct {
const char *prefixname;
const char *prefixval;
} prefixtable[MAXPREFIXES];
static const char *NULLUNIT = "";
static int unitcount;
static int prefixcount;
static int addsubunit(const char *[], const char *);
static int addunit(struct unittype *, const char *, int);
static void cancelunit(struct unittype *);
static int compare(const void *, const void *);
static int compareproducts(const char **, const char **);
static int compareunits(struct unittype *, struct unittype *);
static int compareunitsreciprocal(struct unittype *, struct unittype *);
static int completereduce(struct unittype *);
static void initializeunit(struct unittype *);
static void readerror(int);
static void readunits(const char *);
static int reduceproduct(struct unittype *, int);
static int reduceunit(struct unittype *);
static void showanswer(struct unittype *, struct unittype *);
static void showunit(struct unittype *);
static void sortunit(struct unittype *);
__dead static void usage(void);
static void zeroerror(void);
static char *dupstr(const char *);
static const char *lookupunit(const char *);
static char *
dupstr(const char *str)
{
char *ret;
ret = strdup(str);
if (!ret)
err(3, "Memory allocation error");
return (ret);
}
static void
mywarnx(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (errprefix) {
/* warn to stdout, with errprefix prepended */
printf("%s", errprefix);
vprintf(fmt, args);
printf("%s", "\n");
} else {
/* warn to stderr */
vwarnx(fmt, args);
}
va_end(args);
}
static void
readerror(int linenum)
{
mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum);
}
static void
readunits(const char *userfile)
{
FILE *unitfile;
char line[80], *lineptr;
int len, linenum, i, isdup;
unitcount = 0;
linenum = 0;
if (userfile) {
unitfile = fopen(userfile, "rt");
if (!unitfile)
err(1, "Unable to open units file '%s'", userfile);
}
else {
unitfile = fopen(UNITSFILE, "rt");
if (!unitfile) {
char *direc, *env;
char filename[1000];
char separator[2];
env = getenv("PATH");
if (env) {
if (strchr(env, ';'))
strlcpy(separator, ";",
sizeof(separator));
else
strlcpy(separator, ":",
sizeof(separator));
direc = strtok(env, separator);
while (direc) {
strlcpy(filename, "", sizeof(filename));
strlcat(filename, direc,
sizeof(filename));
strlcat(filename, "/",
sizeof(filename));
strlcat(filename, UNITSFILE,
sizeof(filename));
unitfile = fopen(filename, "rt");
if (unitfile)
break;
direc = strtok(NULL, separator);
}
}
if (!unitfile)
errx(1, "Can't find units file '%s'",
UNITSFILE);
}
}
while (!feof(unitfile)) {
if (!fgets(line, 79, unitfile))
break;
linenum++;
lineptr = line;
if (*lineptr == '/')
continue;
lineptr += strspn(lineptr, " \n\t");
len = strcspn(lineptr, " \n\t");
lineptr[len] = 0;
if (!strlen(lineptr))
continue;
if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
if (prefixcount == MAXPREFIXES) {
mywarnx(
"Memory for prefixes exceeded in line %d",
linenum);
continue;
}
lineptr[strlen(lineptr) - 1] = 0;
for (isdup = 0, i = 0; i < prefixcount; i++) {
if (!strcmp(prefixtable[i].prefixname,
lineptr)) {
isdup = 1;
break;
}
}
if (isdup) {
mywarnx(
"Redefinition of prefix '%s' on line %d ignored",
lineptr, linenum);
continue;
}
prefixtable[prefixcount].prefixname = dupstr(lineptr);
lineptr += len + 1;
if (!strlen(lineptr)) {
readerror(linenum);
continue;
}
lineptr += strspn(lineptr, " \n\t");
len = strcspn(lineptr, "\n\t");
lineptr[len] = 0;
prefixtable[prefixcount++].prefixval = dupstr(lineptr);
}
else { /* it's not a prefix */
if (unitcount == MAXUNITS) {
mywarnx("Memory for units exceeded in line %d",
linenum);
continue;
}
for (isdup = 0, i = 0; i < unitcount; i++) {
if (!strcmp(unittable[i].uname, lineptr)) {
isdup = 1;
break;
}
}
if (isdup) {
mywarnx(
"Redefinition of unit '%s' on line %d ignored",
lineptr, linenum);
continue;
}
unittable[unitcount].uname = dupstr(lineptr);
lineptr += len + 1;
lineptr += strspn(lineptr, " \n\t");
if (!strlen(lineptr)) {
readerror(linenum);
continue;
}
len = strcspn(lineptr, "\n\t");
lineptr[len] = 0;
unittable[unitcount++].uval = dupstr(lineptr);
}
}
fclose(unitfile);
}
static void
initializeunit(struct unittype * theunit)
{
theunit->factor = 1.0;
theunit->numerator[0] = theunit->denominator[0] = NULL;
}
static int
addsubunit(const char *product[], const char *toadd)
{
const char **ptr;
for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
if (ptr >= product + MAXSUBUNITS) {
mywarnx("Memory overflow in unit reduction");
return 1;
}
if (!*ptr)
*(ptr + 1) = 0;
*ptr = dupstr(toadd);
return 0;
}
static void
showunit(struct unittype * theunit)
{
const char **ptr;
int printedslash;
int counter = 1;
printf("\t%.*g", precision, theunit->factor);
for (ptr = theunit->numerator; *ptr; ptr++) {
if (ptr > theunit->numerator && **ptr &&
!strcmp(*ptr, *(ptr - 1)))
counter++;
else {
if (counter > 1)
printf("%s%d", powerstring, counter);
if (**ptr)
printf(" %s", *ptr);
counter = 1;
}
}
if (counter > 1)
printf("%s%d", powerstring, counter);
counter = 1;
printedslash = 0;
for (ptr = theunit->denominator; *ptr; ptr++) {
if (ptr > theunit->denominator && **ptr &&
!strcmp(*ptr, *(ptr - 1)))
counter++;
else {
if (counter > 1)
printf("%s%d", powerstring, counter);
if (**ptr) {
if (!printedslash)
printf(" /");
printedslash = 1;
printf(" %s", *ptr);
}
counter = 1;
}
}
if (counter > 1)
printf("%s%d", powerstring, counter);
printf("\n");
}
static void
zeroerror(void)
{
mywarnx("Unit reduces to zero");
}
/*
Adds the specified string to the unit.
Flip is 0 for adding normally, 1 for adding reciprocal.
Returns 0 for successful addition, nonzero on error.
*/
static int
addunit(struct unittype * theunit, const char *toadd, int flip)
{
char *scratch, *savescr;
char *item;
char *divider, *slash;
int doingtop;
savescr = scratch = dupstr(toadd);
for (slash = scratch + 1; *slash; slash++)
if (*slash == '-' &&
(tolower((unsigned char)*(slash - 1)) != 'e' ||
!strchr(".0123456789", *(slash + 1))))
*slash = ' ';
slash = strchr(scratch, '/');
if (slash)
*slash = 0;
doingtop = 1;
do {
item = strtok(scratch, " *\t\n/");
while (item) {
if (strchr("0123456789.", *item)) {
/* item starts with a number */
char *endptr;
double num;
divider = strchr(item, '|');
if (divider) {
*divider = 0;
num = strtod(item, &endptr);
if (!num) {
zeroerror();
return 1;
}
if (endptr != divider) {
/* "6foo|2" is an error */
mywarnx("Junk before '|'");
return 1;
}
if (doingtop ^ flip)
theunit->factor *= num;
else
theunit->factor /= num;
num = strtod(divider + 1, &endptr);
if (!num) {
zeroerror();
return 1;
}
if (doingtop ^ flip)
theunit->factor /= num;
else
theunit->factor *= num;
if (*endptr) {
/* "6|2foo" is like "6|2 foo" */
item = endptr;
continue;
}
}
else {
num = strtod(item, &endptr);
if (!num) {
zeroerror();
return 1;
}
if (doingtop ^ flip)
theunit->factor *= num;
else
theunit->factor /= num;
if (*endptr) {
/* "3foo" is like "3 foo" */
item = endptr;
continue;
}
}
}
else { /* item is not a number */
int repeat = 1;
if (strchr("23456789",
item[strlen(item) - 1])) {
repeat = item[strlen(item) - 1] - '0';
item[strlen(item) - 1] = 0;
}
for (; repeat; repeat--)
if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
return 1;
}
item = strtok(NULL, " *\t/\n");
}
doingtop--;
if (slash) {
scratch = slash + 1;
}
else
doingtop--;
} while (doingtop >= 0);
free(savescr);
return 0;
}
static int
compare(const void *item1, const void *item2)
{
return strcmp(*(const char * const *) item1,
*(const char * const *) item2);
}
static void
sortunit(struct unittype * theunit)
{
const char **ptr;
int count;
for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
qsort(theunit->numerator, count, sizeof(char *), compare);
for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
qsort(theunit->denominator, count, sizeof(char *), compare);
}
static void
cancelunit(struct unittype * theunit)
{
const char **den, **num;
int comp;
den = theunit->denominator;
num = theunit->numerator;
while (*num && *den) {
comp = strcmp(*den, *num);
if (!comp) {
/* if (*den!=NULLUNIT) free(*den);
if (*num!=NULLUNIT) free(*num);*/
*den++ = NULLUNIT;
*num++ = NULLUNIT;
}
else if (comp < 0)
den++;
else
num++;
}
}
/*
Looks up the definition for the specified unit.
Returns a pointer to the definition or a null pointer
if the specified unit does not appear in the units table.
*/
static char buffer[100]; /* buffer for lookupunit answers with
prefixes */
static const char *
lookupunit(const char *unit)
{
int i;
char *copy;
for (i = 0; i < unitcount; i++) {
if (!strcmp(unittable[i].uname, unit))
return unittable[i].uval;
}
if (unit[strlen(unit) - 1] == '^') {
copy = dupstr(unit);
copy[strlen(copy) - 1] = 0;
for (i = 0; i < unitcount; i++) {
if (!strcmp(unittable[i].uname, copy)) {
strlcpy(buffer, copy, sizeof(buffer));
free(copy);
return buffer;
}
}
free(copy);
}
if (unit[strlen(unit) - 1] == 's') {
copy = dupstr(unit);
copy[strlen(copy) - 1] = 0;
for (i = 0; i < unitcount; i++) {
if (!strcmp(unittable[i].uname, copy)) {
strlcpy(buffer, copy, sizeof(buffer));
free(copy);
return buffer;
}
}
if (copy[strlen(copy) - 1] == 'e') {
copy[strlen(copy) - 1] = 0;
for (i = 0; i < unitcount; i++) {
if (!strcmp(unittable[i].uname, copy)) {
strlcpy(buffer, copy, sizeof(buffer));
free(copy);
return buffer;
}
}
}
free(copy);
}
for (i = 0; i < prefixcount; i++) {
if (!strncmp(prefixtable[i].prefixname, unit,
strlen(prefixtable[i].prefixname))) {
unit += strlen(prefixtable[i].prefixname);
if (!strlen(unit) || lookupunit(unit)) {
strlcpy(buffer, prefixtable[i].prefixval,
sizeof(buffer));
strlcat(buffer, " ", sizeof(buffer));
strlcat(buffer, unit, sizeof(buffer));
return buffer;
}
}
}
return 0;
}
/*
reduces a product of symbolic units to primitive units.
The three low bits are used to return flags:
bit 0 (1) set on if reductions were performed without error.
bit 1 (2) set on if no reductions are performed.
bit 2 (4) set on if an unknown unit is discovered.
*/
#define ERROR 4
static int
reduceproduct(struct unittype * theunit, int flip)
{
const char *toadd;
const char **product;
int didsomething = 2;
if (flip)
product = theunit->denominator;
else
product = theunit->numerator;
for (; *product; product++) {
for (;;) {
if (!strlen(*product))
break;
toadd = lookupunit(*product);
if (!toadd) {
mywarnx("Unknown unit '%s'", *product);
return ERROR;
}
if (strchr(toadd, PRIMITIVECHAR))
break;
didsomething = 1;
if (*product != NULLUNIT) {
free(__UNCONST(*product));
*product = NULLUNIT;
}
if (addunit(theunit, toadd, flip))
return ERROR;
}
}
return didsomething;
}
/*
Reduces numerator and denominator of the specified unit.
Returns 0 on success, or 1 on unknown unit error.
*/
static int
reduceunit(struct unittype * theunit)
{
int ret;
ret = 1;
while (ret & 1) {
ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
if (ret & 4)
return 1;
}
return 0;
}
static int
compareproducts(const char **one, const char **two)
{
while (*one || *two) {
if (!*one && *two != NULLUNIT)
return 1;
if (!*two && *one != NULLUNIT)
return 1;
if (*one == NULLUNIT)
one++;
else if (*two == NULLUNIT)
two++;
else if (*one && *two && strcmp(*one, *two))
return 1;
else
one++, two++;
}
return 0;
}
/* Return zero if units are compatible, nonzero otherwise */
static int
compareunits(struct unittype * first, struct unittype * second)
{
return
compareproducts(first->numerator, second->numerator) ||
compareproducts(first->denominator, second->denominator);
}
static int
compareunitsreciprocal(struct unittype * first, struct unittype * second)
{
return
compareproducts(first->numerator, second->denominator) ||
compareproducts(first->denominator, second->numerator);
}
static int
completereduce(struct unittype * unit)
{
if (reduceunit(unit))
return 1;
sortunit(unit);
cancelunit(unit);
return 0;
}
static void
showanswer(struct unittype * have, struct unittype * want)
{
if (compareunits(have, want)) {
if (compareunitsreciprocal(have, want)) {
printf("conformability error\n");
showunit(have);
showunit(want);
} else {
printf("\treciprocal conversion\n");
printf("\t* %.*g\n\t/ %.*g\n",
precision, 1 / (have->factor * want->factor),
precision, want->factor * have->factor);
}
}
else
printf("\t* %.*g\n\t/ %.*g\n",
precision, have->factor / want->factor,
precision, want->factor / have->factor);
}
static int
listunits(int expand)
{
struct unittype theunit;
const char *thename;
const char *thedefn;
int errors = 0;
int i;
int printexpansion;
/*
* send error and warning messages to stdout,
* and make them look like comments.
*/
errprefix = "/ ";
#if 0 /* debug */
printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n",
expand, precision, unitcount, prefixcount);
#endif
/* 1. Dump all primitive units, e.g. "m !a!", "kg !b!", ... */
printf("/ Primitive units\n");
for (i = 0; i < unitcount; i++) {
thename = unittable[i].uname;
thedefn = unittable[i].uval;
if (thedefn[0] == PRIMITIVECHAR) {
printf("%s\t%s\n", thename, thedefn);
}
}
/* 2. Dump all prefixes, e.g. "yotta- 1e24", "zetta- 1e21", ... */
printf("/ Prefixes\n");
for (i = 0; i < prefixcount; i++) {
printexpansion = expand;
thename = prefixtable[i].prefixname;
thedefn = prefixtable[i].prefixval;
if (expand) {
/*
* prefix names are sometimes identical to unit
* names, so we have to expand thedefn instead of
* expanding thename.
*/
initializeunit(&theunit);
if (addunit(&theunit, thedefn, 0) != 0
|| completereduce(&theunit) != 0) {
errors++;
printexpansion = 0;
mywarnx("Error in prefix '%s-'", thename);
}
}
if (printexpansion) {
printf("%s-", thename);
showunit(&theunit);
} else
printf("%s-\t%s\n", thename, thedefn);
}
/* 3. Dump all other units. */
printf("/ Other units\n");
for (i = 0; i < unitcount; i++) {
printexpansion = expand;
thename = unittable[i].uname;
thedefn = unittable[i].uval;
if (thedefn[0] == PRIMITIVECHAR)
continue;
if (expand) {
/*
* expand thename, not thedefn, so that
* we can catch errors in the name itself.
* e.g. a name that contains a hyphen
* will be interpreted as multiplication.
*/
initializeunit(&theunit);
if (addunit(&theunit, thename, 0) != 0
|| completereduce(&theunit) != 0) {
errors++;
printexpansion = 0;
mywarnx("Error in unit '%s'", thename);
}
}
if (printexpansion) {
printf("%s", thename);
showunit(&theunit);
} else
printf("%s\t%s\n", thename, thedefn);
}
if (errors)
mywarnx("Definitions with errors: %d", errors);
return (errors ? 1 : 0);
}
static void
usage(void)
{
fprintf(stderr,
"\nunits [-Llqv] [-f filename] [[count] from-unit to-unit]\n");
fprintf(stderr, "\n -f specify units file\n");
fprintf(stderr, " -L list units in standardized base units\n");
fprintf(stderr, " -l list units\n");
fprintf(stderr, " -q suppress prompting (quiet)\n");
fprintf(stderr, " -v print version number\n");
exit(3);
}
int
main(int argc, char **argv)
{
struct unittype have, want;
char havestr[81], wantstr[81];
int optchar;
const char *userfile = 0;
int list = 0, listexpand = 0;
int quiet = 0;
while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) {
switch (optchar) {
case 'l':
list = 1;
break;
case 'L':
list = 1;
listexpand = 1;
precision = DBL_DIG;
break;
case 'f':
userfile = optarg;
break;
case 'q':
quiet = 1;
break;
case 'v':
fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
VERSION);
fprintf(stderr, " This program may be freely distributed\n");
usage();
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if ((argc != 3 && argc != 2 && argc != 0)
|| (list && argc != 0))
usage();
if (list)
errprefix = "/ "; /* set this before reading the file */
readunits(userfile);
if (list)
return listunits(listexpand);
if (argc == 3) {
strlcpy(havestr, argv[0], sizeof(havestr));
strlcat(havestr, " ", sizeof(havestr));
strlcat(havestr, argv[1], sizeof(havestr));
argc--;
argv++;
argv[0] = havestr;
}
if (argc == 2) {
strlcpy(havestr, argv[0], sizeof(havestr));
strlcpy(wantstr, argv[1], sizeof(wantstr));
initializeunit(&have);
addunit(&have, havestr, 0);
completereduce(&have);
initializeunit(&want);
addunit(&want, wantstr, 0);
completereduce(&want);
showanswer(&have, &want);
}
else {
if (!quiet)
printf("%d units, %d prefixes\n\n", unitcount,
prefixcount);
for (;;) {
do {
initializeunit(&have);
if (!quiet)
printf("You have: ");
if (!fgets(havestr, 80, stdin)) {
if (!quiet)
putchar('\n');
exit(0);
}
} while (addunit(&have, havestr, 0) ||
completereduce(&have));
do {
initializeunit(&want);
if (!quiet)
printf("You want: ");
if (!fgets(wantstr, 80, stdin)) {
if (!quiet)
putchar('\n');
exit(0);
}
} while (addunit(&want, wantstr, 0) ||
completereduce(&want));
showanswer(&have, &want);
}
}
return (0);
}

651
usr.bin/units/units.lib Normal file
View file

@ -0,0 +1,651 @@
/ $NetBSD: units.lib,v 1.17 2012/12/28 17:57:46 apb Exp $
/ primitive units
m !a!
kg !b!
sec !c!
coul !d!
candela !e!
dollar !f!
bit !h!
erlang !i!
K !j!
/ prefixes
yotta- 1e24
zetta- 1e21
exa- 1e18
peta- 1e15
tera- 1e12
giga- 1e9
mega- 1e6
myria- 1e4
kilo- 1e3
hecto- 1e2
deka- 1e1
deci- 1e-1
centi- 1e-2
milli- 1e-3
micro- 1e-6
nano- 1e-9
pico- 1e-12
femto- 1e-15
atto- 1e-18
zopto- 1e-21
yocto- 1e-24
semi- .5
demi- .5
Y- yotta
Z- zetta
E- exa
P- peta
T- tera
G- giga
M- mega
k- kilo
h- hecto
da- deka
d- deci
c- centi
m- milli
p- pico
f- femto
a- atto
z- zopto
y- yocto
/ constants
fuzz 1
pi 3.14159265358979323846
c 2.99792458e+8 m/sec fuzz
g 9.80665 m/sec2
au 1.49597871e+11 m fuzz
mole 6.022169e+23 fuzz
e 1.6021917e-19 coul fuzz
energy c2
force g
mercury 1.33322e+5 kg/m2-sec2
hg mercury
/ dimensionless
radian .5 / pi
degree 1|180 pi-radian
circle 2 pi-radian
turn 2 pi-radian
revolution turn
rev turn
grade .9 degree
arcdeg 1 degree
arcmin 1|60 arcdeg
ccs 1|36 erlang
arcsec 1|60 arcmin
steradian radian2
sphere 4 pi-steradian
sr steradian
/ Time
second sec
s sec
minute 60 sec
min minute
hour 60 min
hr hour
day 24 hr
da day
week 7 day
year 365.24219879 day fuzz
yr year
month 1|12 year
ms millisec
us microsec
/ Mass
gram millikg
gm gram
mg milligram
metricton kilokg
/ Avoirdupois
lb .45359237 kg
pound lb
lbf lb g
ounce 1|16 lb
oz ounce
dram 1|16 oz
dr dram
grain 1|7000 lb
gr grain
shortton 2000 lb
ton shortton
longton 2240 lb
/ Apothecary
scruple 20 grain
apdram 60 grain
apounce 480 grain
appound 5760 grain
troypound appound
/ Length
meter m
cm centimeter
mm millimeter
km kilometer
nm nanometer
micron micrometer
angstrom decinanometer
inch 2.54 cm
in inch
foot 12 in
feet foot
ft foot
yard 3 ft
yd yard
rod 5.5 yd
rd rod
mile 5280 ft
mi mile
british 1200|3937 m/ft
nmile 1852 m
acre 4840 yd2
cc cm3
liter kilocc
ml milliliter
/ US Liquid
gallon 231 in3
imperial 1.20095
gal gallon
quart 1|4 gal
qt quart
pint 1|2 qt
pt pint
floz 1|16 pt
fldr 1|8 floz
/ US Dry
dry 268.8025 in3/gallon fuzz
peck 8 dry-quart
pk peck
bushel 4 peck
bu bushel
chaldron 36 bushel
/ British
brgallon 277.420 in3 fuzz
brquart 1|4 brgallon
brpint 1|2 brquart
brfloz 1|20 brpint
brpeck 554.84 in3 fuzz
brbushel 4 brpeck
/ Bottles
bottle 750 milliliter
/bottle fifth
miniature 100 milliliter
split 1|4 bottle
half 1|2 bottle
magnum 2 bottle
jeroboam 4 bottle
rehoboam 6 bottle
methuselah 8 bottle
salmanazar 12 bottle
balthazar 16 bottle
nebuchadnezzar 20 bottle
sovereign 34 bottle
/ Bottles - alternate names and spellings
pony split
fillette half
tappithen 3 bottle
rheoboam rehoboam
shalmaneser salmanazar
/ Energy Work
newton kg-m/sec2
nt newton
N newton
joule nt-m
cal 4.1868 joule
/ Electrical
coulomb coul
C coul
ampere coul/sec
amp ampere
watt joule/sec
volt watt/amp
ohm volt/amp
mho /ohm
farad coul/volt
henry sec2/farad
weber volt-sec
/ Light
cd candela
lumen cd sr
lux cd sr/m2
/ Wall Street Journal, July 2, 1993
$ dollar
argentinapeso $
australiadollar .66 $
bahraindinar 2.6522 $
brazilcruzeiro .000019 $
britainpound 1.49 $
canadadollar .77 $
czechkoruna .034 $
chilepeso .0025 $
chinarenminbi .174856 $
colombiapeso .001495 $
denmarkkrone .15 $
ecuadorsucre .000539 $
europeeuro 0.9142 $
greatbritainpound britainpound
hongkongdollar .13 $
hungaryforint .011 $
indiarupee .03211 $
indonesiarupiah .0004782 $
israelshekel .3642 $
japanyen .0093 $
jordandinar 1.4682 $
kuwaitdinar 3.3173 $
lebanonpound .000578 $
malaysiaringgit .338 $
maltalira 2.6042 $
mexicopeso .3205128 $
newzealanddollar .539 $
norwaykrone .139 $
pakistanrupee .037 $
perunewsol .5065 $
philippinespeso .03738 $
polandzloty .000059 $
saudiarabiariyal .26702 $
singaporedollar .6157 $
slovakkoruna .034 $
southafricarand .21 $
southkoreawon .001 $
swedenkrona .13 $
switzerlandfranc .66 $
taiwandollar .038285 $
thailandbaht .03962 $
turkeylira .0000929 $
unitedarabdirham .2723 $
uruguaynewpeso .246852 $
venezuelabolivar .011 $
/ The following currencies are locked to the Euro:
/ <URL:http://www.ecb.int/change/conversion.htm>
belgiumfranc 1|40.3399 euro
germanymark 1|1.95583 euro
spainpeseta 1|166.386 euro
francefranc 1|6.55957 euro
irelandpunt 1|.787564 euro
italylira 1|1936.27 euro
luxembourgfranc 1|40.3399 euro
netherlandsguilder 1|2.20371 euro
austriaschilling 1|13.7603 euro
portugalescudo 1|200.482 euro
finlandmarkka 1|5.94573 euro
greecedrachma 1|340.750 euro
mark germanymark
euro europeeuro
bolivar venezuelabolivar
peseta spainpeseta
rand southafricarand
escudo portugalescudo
newsol perunewsol
guilder netherlandsguilder
hollandguilder netherlandsguilder
peso mexicopeso
yen japanyen
lira italylira
rupee indiarupee
drachma greecedrachma
franc francefranc
markka finlandmarkka
sucre ecuadorsucre
poundsterling britainpound
cruzeiro brazilcruzeiro
/ computer
baud bit/sec
byte 8 bit
block 512 byte
kbyte 1024 byte
megabyte 1024 kbyte
gigabyte 1024 megabyte
meg megabyte
/ Trivia
% 1|100
admiraltyknot 6080 ft/hr
apostilb cd/pi-m2
are 1e+2 m2
arpentcan 27.52 mi
arpentlin 191.835 ft
astronomicalunit au
atmosphere 1.01325e+5 nt/m2
atm atmosphere
atomicmassunit 1.66044e-27 kg fuzz
amu atomicmassunit
bag 94 lb
bakersdozen 13
bar 1e+5 nt/m2
barie 1e-1 nt/m2
barleycorn 1|3 in
barn 1e-28 m2
barrel 42 gal
barye 1e-1 nt/m2
bev 1e+9 e-volt
biot 10 amp
blondel cd/pi-m2
boardfoot 144 in3
bolt 40 yd
bottommeasure 1|40 in
britishthermalunit 1.05506e+3 joule fuzz
btu britishthermalunit
refrigeration 12000 btu/ton-hour
buck dollar
Ci curie
cable 720 ft
caliber 1e-2 in
calorie cal
carat 205 mg
caratgold 1|24
cent centidollar
cental 100 lb
centesimalminute 1e-2 grade
centesimalsecond 1e-4 grade
century 100 year
cfs ft3/sec
chain 66 ft
circularinch 1|4 pi-in2
circularmil 1e-6|4 pi-in2
clusec 1e-8 mm-hg m3/s
coomb 4 bu
cord 128 ft3
cordfoot cord
crith 9.06e-2 gm
cubit 18 in
cup 1|2 pt
curie 3.7e+10 /sec
dalton amu
decade 10 yr
diopter /m
displacementton 35 ft3
doppelzentner 100 kg
dozen 12
drop .03 cm3
dyne cm-gm/sec2
electronvolt e-volt
ell 45 in
engineerschain 100 ft
engineerslink 100|100 ft
equivalentfootcandle lumen/pi-ft2
equivalentlux lumen/pi-m2
equivalentphot cd/pi-cm2
erg cm2-gm/sec2
ev e-volt
faraday 9.652e+4 coul
fathom 6 ft
fermi 1e-15 m
fifth 4|5 qt
fin 5 dollar
finger 7|8 in
firkin 9 gal
footcandle lumen/ft2
footlambert cd/pi-ft2
fortnight 14 da
franklin 3.33564e-10 coul
frigorie kilocal
furlong 220 yd
Gy gray
galileo 1e-2 m/sec2
gamma 1e-9 weber/m2
gauss 1e-4 weber/m2
geodeticfoot british-ft
geographicalmile 1852 m
gilbert 7.95775e-1 amp
gill 1|4 pt
gray joule/kg
gross 144
gunterschain 22 yd
hand 4 in
hectare 1e+4 m2
hefnercandle .92 cd
hertz /sec
Hz hertz
hogshead 63 gallon
hd hogshead
homestead 1|4 mi2
horsepower 550 ft-lb-g/sec
hp horsepower
hyl gm force sec2/m
hz /sec
imaginarycubicfoot 1.4 ft3
karat 1|24
kcal kilocal
kcalorie kilocal
kev 1e+3 e-volt
key kg
khz 1e+3 /sec
kilderkin 18 gal
knot nmile/hr
lambert cd/pi-cm2
langley cal/cm2
last 80 bu
league 3 mi
lightyear c-yr
line 1|12 in
link 66|100 ft
longhundredweight 112 lb
longquarter 28 lb
lusec 1e-6 mm-hg m3/s
mach 331.46 m/sec
marineleague 3 nmile
maxwell 1e-8 weber
metriccarat 200 mg
mgd megagal/day
mh millihenry
mhz 1e+6 /sec
mil 1e-3 in
millennium 1000 year
minersinch 1.5 ft3/min
minim 1|60 fldr
mo month
mpg mile/gal
mph mile/hr
nail 1|16 yd
nauticalmile nmile
nit cd/m2
noggin 1|8 qt
nox 1e-3 lux
ns nanosec
oersted 2.5e+2 pi-amp/m
oe oersted
pace 36 in
palm 3 in
parasang 3.5 mi
parsec au-radian/arcsec
pascal nt/m2
pc parsec
pennyweight 1|20 oz
pwt pennyweight
percent %
perch rd
pf picofarad
phot lumen/cm2
pica 1|6 in
pieze 1e+3 nt/m2
pipe 4 barrel
point 1|72 in
poise gm/cm-sec
pole rd
poundal ft-lb/sec2
pdl poundal
proof 1|200
psi lb-g/in2
quarter 9 in
quartersection 1|4 mi2
quintal 100 kg
quire 25
R roentgen
rackunit 1.75 in
rad 100 erg/gm
ream 500
registerton 100 ft3
rem 1e-2 sievert
rhe 10 m2/nt-sec
roentgen 2.58e-4 coulomb/kg
rontgen roentgen
rood 1.21e+3 yd
rope 20 ft
RU rackunit
rutherford 1e+6 /sec
rydberg 1.36054e+1 ev
Sv sievert
sabin 1 ft2
sack 3 bu
score 20
seam 8 bu
section mi2
shed 1e-24 barn
shippington 40 ft3
shorthundredweight 100 lb
shortquarter 25 lb
siemens /ohm
sievert joule/kg
sigma microsec
skein 120 yd
skot 1e-3 apostilb
slug lb-g-sec2/ft
smoot 67 in
span 9 in
spat 4 pi sr
spindle 14400 yd
square 100 ft2
stere m3
sthene 1e+3 nt
stilb cd/cm2
stoke 1e-4 m2/sec
stone 14 lb
strike 2 bu
surveyfoot british-ft
surveyyard 3 surveyfoot
surveyorschain 66 ft
surveyorslink 66|100 ft
tablespoon 4 fldr
teaspoon 4|3 fldr
tesla weber/m2
therm 1e+5 btu
thermie 1e+6 cal
timberfoot ft3
tnt 4.6e+6 m2/sec2
tonne 1e+6 gm
torr mm hg
township 36 mi2
tun 8 barrel
U rackunit
water gram g / cc
wey 40 bu
weymass 252 lb
Xunit 1.00202e-13 m
k 1.38047e-16 erg/degC
degC K
kelvin K
brewster 1e-12 m2/newton
degF 5|9 degC
degreesrankine degF
degrankine degreesrankine
degreerankine degF
degreaumur 10|8 degC
drachm 60 grain
poncelet 100 kg m g / sec
denier .05|450 gram / m
tex .001 gram / m
englishell 45 inch
scottishell 37.2 inch
flemishell 27 inch
planck 6.626e-34 joule-sec
hbar 1.055e-34 joule-sec
electronmass 9.1095e-31 kg
protonmass 1.6726e-27 kg
neutronmass 1.6606e-27 kg
V volt
eV e V
bohrradius hbar2-C2/8.988e9 N m2-e2-electronmass
becquerel 1|3.7e10 curie
fresnel 1e12 hertz
statcoul 1|2.99792458e9 coul
statamp 1|2.99792458e9 amp
statvolt 2.99792458e2 volt
statcoulomb statcoul
statampere statamp
debye 3.336e-30 coul-m
pulsatance 2 pi/sec
rpm rev/minute
rps rev/sec
kilohm kiloohm
megohm megaohm
siderealyear 365.256360417 day
siderealday 23.934469444 hour
siderealhour 1|24 siderealday
lunarmonth 29.5305555 day
synodicmonth lunarmonth
siderealmonth 27.32152777 day
tropicalyear year
solaryear year
lunaryear 12 lunarmonth
cran 37.5 brgallon
kip 1000 lbf
frenchfoot 16|15 ft
frenchfeet frenchfoot
toise 6 frenchfeet
candle 1.02 candela
militarypace 2.5 feet
metre meter
litre liter
gramme gram
iudiptheria 62.8 microgram
iupenicillin .6 microgram
iuinsulin 41.67 microgram
cottonyarncount 2520 ft/pound
linenyarncount 900 ft/pound
worstedyarncount 1680 ft/pound
metricyarncount meter/gram
jewlerspoint 2 milligram