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

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