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
|
||||
.Dt UNITS 1
|
||||
.Os
|
||||
|
@ -8,7 +8,7 @@
|
|||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl f Ar filename
|
||||
.Op Fl qv
|
||||
.Op Fl lLqv
|
||||
.Oo
|
||||
.Op Ar count
|
||||
.Ar from-unit to-unit
|
||||
|
@ -25,6 +25,24 @@ 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.
|
||||
|
|
|
@ -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)
|
||||
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -39,6 +40,13 @@
|
|||
|
||||
#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 {
|
||||
|
@ -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
|
||||
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;
|
||||
if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
|
||||
if (prefixcount == MAXPREFIXES) {
|
||||
warnx("Memory for prefixes exceeded in line %d",
|
||||
linenum);
|
||||
mywarnx(
|
||||
"Memory for prefixes exceeded in line %d",
|
||||
linenum);
|
||||
continue;
|
||||
}
|
||||
lineptr[strlen(lineptr) - 1] = 0;
|
||||
|
@ -181,7 +208,7 @@ readunits(const char *userfile)
|
|||
}
|
||||
}
|
||||
if (isdup) {
|
||||
warnx(
|
||||
mywarnx(
|
||||
"Redefinition of prefix '%s' on line %d ignored",
|
||||
lineptr, linenum);
|
||||
continue;
|
||||
|
@ -199,7 +226,7 @@ readunits(const char *userfile)
|
|||
}
|
||||
else { /* it's not a prefix */
|
||||
if (unitcount == MAXUNITS) {
|
||||
warnx("Memory for units exceeded in line %d",
|
||||
mywarnx("Memory for units exceeded in line %d",
|
||||
linenum);
|
||||
continue;
|
||||
}
|
||||
|
@ -210,7 +237,7 @@ readunits(const char *userfile)
|
|||
}
|
||||
}
|
||||
if (isdup) {
|
||||
warnx(
|
||||
mywarnx(
|
||||
"Redefinition of unit '%s' on line %d ignored",
|
||||
lineptr, linenum);
|
||||
continue;
|
||||
|
@ -244,7 +271,7 @@ addsubunit(const char *product[], const char *toadd)
|
|||
|
||||
for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
|
||||
if (ptr >= product + MAXSUBUNITS) {
|
||||
warnx("Memory overflow in unit reduction");
|
||||
mywarnx("Memory overflow in unit reduction");
|
||||
return 1;
|
||||
}
|
||||
if (!*ptr)
|
||||
|
@ -260,7 +287,7 @@ showunit(struct unittype * theunit)
|
|||
int printedslash;
|
||||
int counter = 1;
|
||||
|
||||
printf("\t%.8g", theunit->factor);
|
||||
printf("\t%.*g", precision, theunit->factor);
|
||||
for (ptr = theunit->numerator; *ptr; ptr++) {
|
||||
if (ptr > theunit->numerator && **ptr &&
|
||||
!strcmp(*ptr, *(ptr - 1)))
|
||||
|
@ -301,7 +328,7 @@ showunit(struct unittype * theunit)
|
|||
static 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) {
|
||||
/* "6foo|2" is an error */
|
||||
warnx("Junk between number "
|
||||
"and '|'");
|
||||
mywarnx("Junk before '|'");
|
||||
return 1;
|
||||
}
|
||||
if (doingtop ^ flip)
|
||||
|
@ -562,7 +588,7 @@ reduceproduct(struct unittype * theunit, int flip)
|
|||
break;
|
||||
toadd = lookupunit(*product);
|
||||
if (!toadd) {
|
||||
printf("unknown unit '%s'\n", *product);
|
||||
mywarnx("Unknown unit '%s'", *product);
|
||||
return ERROR;
|
||||
}
|
||||
if (strchr(toadd, PRIMITIVECHAR))
|
||||
|
@ -660,15 +686,109 @@ showanswer(struct unittype * have, struct unittype * want)
|
|||
showunit(want);
|
||||
} else {
|
||||
printf("\treciprocal conversion\n");
|
||||
printf("\t* %.8g\n\t/ %.8g\n", 1 / (have->factor * want->factor),
|
||||
want->factor * have->factor);
|
||||
printf("\t* %.*g\n\t/ %.*g\n",
|
||||
precision, 1 / (have->factor * want->factor),
|
||||
precision, want->factor * have->factor);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
|
||||
want->factor / have->factor);
|
||||
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
|
||||
*/
|
||||
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
|
||||
usage(void)
|
||||
|
@ -689,10 +809,19 @@ main(int argc, char **argv)
|
|||
char havestr[81], wantstr[81];
|
||||
int optchar;
|
||||
const char *userfile = 0;
|
||||
int list = 0, listexpand = 0;
|
||||
int quiet = 0;
|
||||
|
||||
while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
|
||||
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;
|
||||
|
@ -713,11 +842,18 @@ main(int argc, char **argv)
|
|||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (argc != 3 && argc != 2 && argc != 0)
|
||||
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));
|
||||
|
|
Loading…
Reference in New Issue