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:
apb 2013-01-01 11:51:55 +00:00
parent 55f985ffa7
commit c6933fca67
2 changed files with 175 additions and 21 deletions

View File

@ -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.

View File

@ -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));