Module Name: src
Committed By: apb
Date: Tue Jan 1 11:51:56 UTC 2013
Modified Files:
src/usr.bin/units: units.1 units.c
Log Message:
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).
To generate a diff of this commit:
cvs rdiff -u -r1.18 -r1.19 src/usr.bin/units/units.1
cvs rdiff -u -r1.20 -r1.21 src/usr.bin/units/units.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.bin/units/units.1
diff -u src/usr.bin/units/units.1:1.18 src/usr.bin/units/units.1:1.19
--- src/usr.bin/units/units.1:1.18 Fri Dec 28 13:25:25 2012
+++ src/usr.bin/units/units.1 Tue Jan 1 11:51:55 2013
@@ -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
.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.
Index: src/usr.bin/units/units.c
diff -u src/usr.bin/units/units.c:1.20 src/usr.bin/units/units.c:1.21
--- src/usr.bin/units/units.c:1.20 Tue Jan 1 11:44:00 2013
+++ src/usr.bin/units/units.c Tue Jan 1 11:51:55 2013
@@ -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 ([email protected])
@@ -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 {
@@ -98,9 +106,27 @@ 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
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
}
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,
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, struc
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));