tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Add -l/-L (list) options to units(1)



The attached patch adds -l and -L options to units(1), to make it list the internal database. Here's the relevant part of the man page:

       -l or -L      List all unit definitions to the standard output,
                     instead of performing any conversions.  The result
                     may include error messages and comments, beginning
                     with `/'.

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

                     With the -L option, all unit definitions will be
                     reduced to a form that depends on only a few primi-
                     tive units (such as m, kg, sec).

The errors that I have just fixed in units.lib were found by running "units -L". I suggest that it would be good practice to compare the output from "units -L" before and after making changes to units.lib.

--apb (Alan Barrett)
Index: units.c
===================================================================
--- units.c     20 Mar 2012 20:34:59 -0000      1.18
+++ units.c     28 Dec 2012 13:33:16 -0000
@@ -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,10 @@
 
 #define PRIMITIVECHAR '!'
 
+static int precision = 8;              /* for printf with "%.*g" format */
+
+static const char *errorprefix = "";   /* printed before error messages */
+
 static const char *powerstring = "^";
 
 static struct {
@@ -249,7 +254,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)))
@@ -534,7 +539,8 @@ reduceproduct(struct unittype * theunit,
                                break;
                        toadd = lookupunit(*product);
                        if (!toadd) {
-                               printf("unknown unit '%s'\n", *product);
+                               printf("%sunknown unit '%s'\n", errorprefix,
+                                   *product);
                                return ERROR;
                        }
                        if (strchr(toadd, PRIMITIVECHAR))
@@ -627,20 +633,100 @@ showanswer(struct unittype * have, struc
 {
        if (compareunits(have, want)) {
                if (compareunitsreciprocal(have, want)) {
-                       printf("conformability error\n");
+                       printf("%sconformability error\n", errorprefix);
                        showunit(have);
                        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;
+
+#if 0 /* debug */
+       printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n",
+           expand, precision, unitcount, prefixcount);
+#endif
+
+       errorprefix = "/ ";
+
+       /* 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++) {
+               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++;
+                               printf("%serror in prefix '%s-'\n",
+                                   errorprefix, thename);
+                       }
+                       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++) {
+               thename = unittable[i].uname;
+               thedefn = unittable[i].uval;
+               if (thedefn[0] != PRIMITIVECHAR) {
+                       if (expand) {
+                               initializeunit(&theunit);
+                               if (addunit(&theunit, thedefn, 0) != 0
+                                   || completereduce(&theunit) != 0) {
+                                       errors++;
+                                       printf("%serror in unit '%s'\n",
+                                           errorprefix, thename);
+                               }
+                               printf("%s", thename);
+                               showunit(&theunit);
+                       } else {
+                               printf("%s\t%s\n", thename, thedefn);
+                       }
+               }
+       }
+
+       if (errors) {
+               printf("%s%d errors\n", errorprefix, errors);
+       }
+       return (errors ? 1 : 0);
+}
 
 static void
 usage(void)
@@ -661,10 +747,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;
@@ -690,6 +785,12 @@ main(int argc, char **argv)
 
        readunits(userfile);
 
+       if (list) {
+               if (argc != 0)
+                       usage();
+               return listunits(listexpand);
+       }
+
        if (argc == 3) {
                strlcpy(havestr, argv[0], sizeof(havestr));
                strlcat(havestr, " ", sizeof(havestr));
Index: units.1
===================================================================
--- units.1     28 Dec 2012 13:25:25 -0000      1.18
+++ units.1     28 Dec 2012 13:33:16 -0000
@@ -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.


Home | Main Index | Thread Index | Old Index