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 (adr...@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 {
@@ -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));

Reply via email to