Add -l and -L options to units(1). "-l" simply lists all unit
definitions, while "-L" alsoreduces them to depend only on a few primitive units (such as m, kg, sec).
This commit is contained in:
parent
55f985ffa7
commit
c6933fca67
@ -1,4 +1,4 @@
|
|||||||
.\" $NetBSD: units.1,v 1.18 2012/12/28 13:25:25 apb Exp $
|
.\" $NetBSD: units.1,v 1.19 2013/01/01 11:51:55 apb Exp $
|
||||||
.Dd December 28, 2012
|
.Dd December 28, 2012
|
||||||
.Dt UNITS 1
|
.Dt UNITS 1
|
||||||
.Os
|
.Os
|
||||||
@ -8,7 +8,7 @@
|
|||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl f Ar filename
|
.Op Fl f Ar filename
|
||||||
.Op Fl qv
|
.Op Fl lLqv
|
||||||
.Oo
|
.Oo
|
||||||
.Op Ar count
|
.Op Ar count
|
||||||
.Ar from-unit to-unit
|
.Ar from-unit to-unit
|
||||||
@ -25,6 +25,24 @@ The following options and arguments are supported:
|
|||||||
.Bl -tag -width "-fXfilenameX" -offset indent
|
.Bl -tag -width "-fXfilenameX" -offset indent
|
||||||
.It Fl f Ar filename
|
.It Fl f Ar filename
|
||||||
Specifies the name of the units data file to load.
|
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
|
.It Fl q
|
||||||
Suppresses prompting of the user for units and the display of statistics
|
Suppresses prompting of the user for units and the display of statistics
|
||||||
about the number of units loaded.
|
about the number of units loaded.
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* $NetBSD: units.c,v 1.20 2013/01/01 11:44:00 apb Exp $ */
|
/* $NetBSD: units.c,v 1.21 2013/01/01 11:51:55 apb Exp $ */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
|
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
|
||||||
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <float.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -39,6 +40,13 @@
|
|||||||
|
|
||||||
#define PRIMITIVECHAR '!'
|
#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 const char *powerstring = "^";
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@ -97,10 +105,28 @@ dupstr(const char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
readerror(int linenum)
|
readerror(int linenum)
|
||||||
{
|
{
|
||||||
warnx("Error in units file '%s' line %d", UNITSFILE, linenum);
|
mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -168,8 +194,9 @@ readunits(const char *userfile)
|
|||||||
continue;
|
continue;
|
||||||
if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
|
if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
|
||||||
if (prefixcount == MAXPREFIXES) {
|
if (prefixcount == MAXPREFIXES) {
|
||||||
warnx("Memory for prefixes exceeded in line %d",
|
mywarnx(
|
||||||
linenum);
|
"Memory for prefixes exceeded in line %d",
|
||||||
|
linenum);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
lineptr[strlen(lineptr) - 1] = 0;
|
lineptr[strlen(lineptr) - 1] = 0;
|
||||||
@ -181,7 +208,7 @@ readunits(const char *userfile)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isdup) {
|
if (isdup) {
|
||||||
warnx(
|
mywarnx(
|
||||||
"Redefinition of prefix '%s' on line %d ignored",
|
"Redefinition of prefix '%s' on line %d ignored",
|
||||||
lineptr, linenum);
|
lineptr, linenum);
|
||||||
continue;
|
continue;
|
||||||
@ -199,7 +226,7 @@ readunits(const char *userfile)
|
|||||||
}
|
}
|
||||||
else { /* it's not a prefix */
|
else { /* it's not a prefix */
|
||||||
if (unitcount == MAXUNITS) {
|
if (unitcount == MAXUNITS) {
|
||||||
warnx("Memory for units exceeded in line %d",
|
mywarnx("Memory for units exceeded in line %d",
|
||||||
linenum);
|
linenum);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -210,7 +237,7 @@ readunits(const char *userfile)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isdup) {
|
if (isdup) {
|
||||||
warnx(
|
mywarnx(
|
||||||
"Redefinition of unit '%s' on line %d ignored",
|
"Redefinition of unit '%s' on line %d ignored",
|
||||||
lineptr, linenum);
|
lineptr, linenum);
|
||||||
continue;
|
continue;
|
||||||
@ -244,7 +271,7 @@ addsubunit(const char *product[], const char *toadd)
|
|||||||
|
|
||||||
for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
|
for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
|
||||||
if (ptr >= product + MAXSUBUNITS) {
|
if (ptr >= product + MAXSUBUNITS) {
|
||||||
warnx("Memory overflow in unit reduction");
|
mywarnx("Memory overflow in unit reduction");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (!*ptr)
|
if (!*ptr)
|
||||||
@ -260,7 +287,7 @@ showunit(struct unittype * theunit)
|
|||||||
int printedslash;
|
int printedslash;
|
||||||
int counter = 1;
|
int counter = 1;
|
||||||
|
|
||||||
printf("\t%.8g", theunit->factor);
|
printf("\t%.*g", precision, theunit->factor);
|
||||||
for (ptr = theunit->numerator; *ptr; ptr++) {
|
for (ptr = theunit->numerator; *ptr; ptr++) {
|
||||||
if (ptr > theunit->numerator && **ptr &&
|
if (ptr > theunit->numerator && **ptr &&
|
||||||
!strcmp(*ptr, *(ptr - 1)))
|
!strcmp(*ptr, *(ptr - 1)))
|
||||||
@ -301,7 +328,7 @@ showunit(struct unittype * theunit)
|
|||||||
static void
|
static void
|
||||||
zeroerror(void)
|
zeroerror(void)
|
||||||
{
|
{
|
||||||
warnx("Unit reduces to zero");
|
mywarnx("Unit reduces to zero");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -347,8 +374,7 @@ addunit(struct unittype * theunit, const char *toadd, int flip)
|
|||||||
}
|
}
|
||||||
if (endptr != divider) {
|
if (endptr != divider) {
|
||||||
/* "6foo|2" is an error */
|
/* "6foo|2" is an error */
|
||||||
warnx("Junk between number "
|
mywarnx("Junk before '|'");
|
||||||
"and '|'");
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (doingtop ^ flip)
|
if (doingtop ^ flip)
|
||||||
@ -562,7 +588,7 @@ reduceproduct(struct unittype * theunit, int flip)
|
|||||||
break;
|
break;
|
||||||
toadd = lookupunit(*product);
|
toadd = lookupunit(*product);
|
||||||
if (!toadd) {
|
if (!toadd) {
|
||||||
printf("unknown unit '%s'\n", *product);
|
mywarnx("Unknown unit '%s'", *product);
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
if (strchr(toadd, PRIMITIVECHAR))
|
if (strchr(toadd, PRIMITIVECHAR))
|
||||||
@ -660,15 +686,109 @@ showanswer(struct unittype * have, struct unittype * want)
|
|||||||
showunit(want);
|
showunit(want);
|
||||||
} else {
|
} else {
|
||||||
printf("\treciprocal conversion\n");
|
printf("\treciprocal conversion\n");
|
||||||
printf("\t* %.8g\n\t/ %.8g\n", 1 / (have->factor * want->factor),
|
printf("\t* %.*g\n\t/ %.*g\n",
|
||||||
want->factor * have->factor);
|
precision, 1 / (have->factor * want->factor),
|
||||||
|
precision, want->factor * have->factor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
|
printf("\t* %.*g\n\t/ %.*g\n",
|
||||||
want->factor / have->factor);
|
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
|
||||||
|
*/
|
||||||
|
initializeunit(&theunit);
|
||||||
|
if (addunit(&theunit, thedefn/*XXX*/, 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
|
static void
|
||||||
usage(void)
|
usage(void)
|
||||||
@ -689,10 +809,19 @@ main(int argc, char **argv)
|
|||||||
char havestr[81], wantstr[81];
|
char havestr[81], wantstr[81];
|
||||||
int optchar;
|
int optchar;
|
||||||
const char *userfile = 0;
|
const char *userfile = 0;
|
||||||
|
int list = 0, listexpand = 0;
|
||||||
int quiet = 0;
|
int quiet = 0;
|
||||||
|
|
||||||
while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
|
while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) {
|
||||||
switch (optchar) {
|
switch (optchar) {
|
||||||
|
case 'l':
|
||||||
|
list = 1;
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
list = 1;
|
||||||
|
listexpand = 1;
|
||||||
|
precision = DBL_DIG;
|
||||||
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
userfile = optarg;
|
userfile = optarg;
|
||||||
break;
|
break;
|
||||||
@ -713,11 +842,18 @@ main(int argc, char **argv)
|
|||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
if (argc != 3 && argc != 2 && argc != 0)
|
if ((argc != 3 && argc != 2 && argc != 0)
|
||||||
|
|| (list && argc != 0))
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
|
if (list)
|
||||||
|
errprefix = "/ "; /* set this before reading the file */
|
||||||
|
|
||||||
readunits(userfile);
|
readunits(userfile);
|
||||||
|
|
||||||
|
if (list)
|
||||||
|
return listunits(listexpand);
|
||||||
|
|
||||||
if (argc == 3) {
|
if (argc == 3) {
|
||||||
strlcpy(havestr, argv[0], sizeof(havestr));
|
strlcpy(havestr, argv[0], sizeof(havestr));
|
||||||
strlcat(havestr, " ", sizeof(havestr));
|
strlcat(havestr, " ", sizeof(havestr));
|
||||||
|
Loading…
Reference in New Issue
Block a user