units: Add units command
authorAlan Cox <alan@etchedpixels.co.uk>
Mon, 16 May 2016 20:58:01 +0000 (21:58 +0100)
committerAlan Cox <alan@etchedpixels.co.uk>
Mon, 16 May 2016 20:58:01 +0000 (21:58 +0100)
Some FIXME's left, and need data files from Coherent

Applications/MWC/cmd/Makefile
Applications/MWC/cmd/units.c [new file with mode: 0644]

index 94f5860..13480a3 100644 (file)
@@ -13,7 +13,7 @@ PROGLOAD=`(cat ../../Kernel/platform/config.h; echo PROGLOAD) | cpp -E | tail -n
 
 SRCSNS = expr.c test.c
 
-SRCS  = ac.c almanac.c at.c col.c deroff.c find.c moo.c pr.c tar.c ttt.c
+SRCS  = ac.c almanac.c at.c col.c deroff.c find.c moo.c pr.c tar.c ttt.c units.c
 
 SRCSBAD = calendar.c m4.c
 
diff --git a/Applications/MWC/cmd/units.c b/Applications/MWC/cmd/units.c
new file mode 100644 (file)
index 0000000..779de1f
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * $Header: /newbits/bin/units.c,v 1.2 89/02/22 05:09:42 bin Exp $
+ * $Log:       /newbits/bin/units.c,v $
+ * Revision 1.2        89/02/22  05:09:42      bin
+ * Changed memory allocation to work better on the atari.
+ * Changed where it looks for units and binary units, using path.
+ *
+ * FIXME: Check unit codes versus man page/POSIX/etc
+ * FIXME: split compiler from units for size/stdio and to remove setuidness
+ * 
+ */
+static char    *revision = "$Revision 1.1 $";
+static char *header =
+       "$Header: /newbits/bin/units.c,v 1.2 89/02/22 05:09:42 bin Exp $";
+
+/*
+ * units -- do multiplicative unit conversions
+ * td 80.09.04
+ * Modified to keep the intermediate format in a file and
+ * update it automatically when it has changed.
+ * (NOTE: program is setuid to bin)
+ * rec 84.08.13
+ * Changed Waiting for Godot ... to Rebuilding %s from %s.
+ * (Nota Bene:
+ *     cc -o units -f units.c
+ *     chown bin /bin/units
+ *     chmod 4755 /bin/units
+ * )
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/stat.h>
+
+#define PATHSIZE 64
+#define        NDIM    12
+#define        NUNITS  900
+#define        NBUF    256             /* Length of longest line */
+#define        UMAGIC  0123456         /* Magic number written in binary header */
+
+/*
+ * Header for the units file.
+ * This  tells that it is the real
+ * thing and how much to read in.
+ */
+typedef        struct  HDR {
+       unsigned h_magic;
+       unsigned h_nunits;
+       unsigned h_ssize;               /* String space size */
+}      HDR;
+
+HDR    hdr;
+
+typedef        struct  UNIT {
+       char    *u_name;
+       double  u_val;
+       char    u_dim[NDIM];
+}      UNIT;
+
+UNIT   *units;
+struct stat    sb;
+char   ufile[] = "units";
+char   binufile[] = "binunits";
+const char *unamep;
+const char *bunamep;
+char   buf[NBUF];
+char   inbuf[BUFSIZ];
+
+int nunits;
+int    uflag;                  /* Update information only */
+FILE *fd;
+int peekc = EOF;
+int lastc = EOF;
+int lineno = 1;
+
+/*
+ * get contiguous memory.
+ *
+ * FIXME: kill stdio & malloc usage
+ */
+char *alloc(unsigned int nb)
+{
+#if 0 
+       register char *rp;
+       if((rp = sbrk(nb)) == ((char *)-1))
+               errx(1, "out of memory");
+       return (rp);
+#else 
+       char *rp = malloc(nb);
+       if (rp == NULL)
+               errx(1, "out of memory");
+       return rp;
+#endif
+}
+
+int nextc(void)
+{
+       register int c;
+
+       if (peekc != EOF) {
+               c = peekc;
+               peekc = EOF;
+               return (c);
+       }
+       if (lastc == '\n')
+               lineno++;
+       lastc = getc(fd);
+       if (lastc == '#') {     /* Eat a comment */
+               do {
+                       lastc = getc(fd);
+               } while(lastc!='\n' && lastc!=EOF);
+       }
+       return (lastc);
+}
+
+char *getname(void)
+{
+       register char *s, *t;
+       register int c;
+       register char *v;
+
+       do {
+               c = nextc();
+       } while(c==' ' || c=='\n' || c=='\t');
+       s = buf;
+       while(c!=' ' && c!='\t' && c!='\n' && c!=EOF) {
+               *s++ = c;
+               c = nextc();
+       }
+       *s = '\0';
+       peekc = c;
+       v = t = alloc(strlen(buf)+1);
+       s = buf;
+       while (*t++ = *s++)
+               ;
+       return (v);
+}
+
+/*
+ * Attempt to read in the already-stored
+ * binary information.  Return non-zero if
+ * successful.
+ *
+ * if(binary and ascii)
+ *      if(binary out of date)
+ *              rebuild
+ *      else
+ *             use binary
+ * if(binary and no ascii)
+ *     use binary
+ * if(ascii and no binary)
+ *     rebuild
+ * crash
+ */
+int binary(void)
+{
+       register char *sstart;
+       register int n;
+       register int bfd;
+       time_t timeasc;
+
+       unamep  = "/usr/lib/units/units.src";
+       bunamep = "/usr/lib/units/units.dat";
+       
+       if(!stat(unamep, &sb)) { /* binary and ascii found */
+               timeasc = sb.st_mtime;
+               if(stat(bunamep, &sb) || (timeasc > sb.st_mtime))
+                       return(0);      /* bin file out of date */
+       }
+       if(uflag)       /* update only */
+               return(0);
+       if((bfd = open(bunamep, 0)) < 0)
+               return (0);
+       if(read(bfd, &hdr, sizeof(hdr)) != sizeof(hdr))
+               goto bad;
+       if(hdr.h_magic != UMAGIC)
+               goto bad;
+       nunits = hdr.h_nunits;
+       sstart = alloc(hdr.h_ssize);
+       if (read(bfd, sstart, hdr.h_ssize) != hdr.h_ssize)
+               goto bad;
+       units = (UNIT *)alloc(n = nunits*sizeof(UNIT));
+       if (read(bfd, units, n) != n)
+               goto bad;
+       for (n=0; n!=nunits; n++)
+               units[n].u_name += (long)sstart;
+       close(bfd);
+       return (1);
+bad:
+       close(bfd);
+       return (0);
+}
+
+/*
+ * Check for any occurrences of
+ * the character `c' in string `s'.
+ *
+ * FIXME: use strchr
+ */
+
+int anyc(char c, char *s)
+{
+       while (*s != '\0')
+               if (c == *s++)
+                       return (1);
+       return (0);
+}
+
+/*
+ * Return non-zero if string `s' is the
+ * same or plural as string `t'.
+ */
+int eqplural(char *s, char *t)
+{
+       while (*t != '\0')
+               if (*s++ != *t++)
+                       return (0);
+       return (*s=='\0' || (*s++=='s' && *s=='\0'));
+}
+
+double ipow(double d, int n)
+{
+       double v;
+
+       v = 1.;
+       if (n < 0) {
+               d = 1./d;
+               n = -n;
+       }
+       while (n) {
+               v *= d;
+               --n;
+       }
+       return (v);
+}
+
+struct{
+       const char *prefix;
+       double factor;
+}pre[]={
+/*FIXME: Check v POSIX and man units */
+{"femto",      1e-15},
+{"pico",       1e-12},
+{"nano",       1e-9},
+{"micro",      1e-6},
+{"milli",      1e-3},
+{"centi",      1e-2},
+{"deci",       1e-1},
+{"hemi",       .5},
+{"demi",       .5},
+{"semi",       .5},
+{"sesqui",     1.5},
+{"deca",       1e1},
+{"hecto",      1e2},
+{"kilo",       1e3},
+{"myria",      1e5},
+{"mega",       1e6},
+{"giga",       1e9},
+{"tera",       1e12},
+{NULL,         1.0}
+};
+
+/*
+ * Return the string stripped of its
+ * prefix (if any).  Set factor
+ * to the multiplicative factor indicated by
+ * the prefix found.
+ */
+char *prefix(const char *str, double *factor)
+{
+       register const char *s, *t;
+       register int i;
+
+       for (i=0; pre[i].prefix!=NULL; i++) {
+               s = pre[i].prefix;
+               t = str;
+               while (*s != '\0')
+                       if (*s++ != *t++)
+                               break;
+               if (*s == '\0') {
+                       *factor = *factor * pre[i].factor;
+                       return (t);
+               }
+       }
+       return(NULL);
+}
+
+int getunit(UNIT *u, char *prompt)
+{
+       register int c;
+       register char *s;
+       register int i;
+       int j, expon, digit, div, pow;
+       double factor;
+
+Again:
+       if (prompt != NULL)
+               printf("%s", prompt);
+       u->u_val = 1.;
+       for (i=0; i != NDIM; i++)
+               u->u_dim[i] = 0;
+       div = 0;
+       pow = 1;
+       for(;;)switch(c=nextc()){
+       case ' ':
+       case '\t':
+               break;
+       case '\n':
+               return (1);
+       case EOF:
+               return (0);
+       case '0':case '1':case '2':case '3':case '4':
+       case '5':case '6':case '7':case '8':case '9':
+       case '.':case '-':case '+':
+               /*
+                * a palpable number
+                */
+               s = buf;
+               if (c == '+')
+                       c = nextc();
+               digit = 0;
+               while (c>='0' && c<='9') {
+                       *s++ = c;
+                       c = nextc();
+                       digit++;
+               }
+               if (c == '.') {
+                       *s++ = c;
+                       while ((c=nextc())>='0' && c<='9') {
+                               *s++ = c;
+                               digit++;
+                       }
+               }
+               if (!digit) {
+               Badnumber:
+                       *s = '\0';
+                       fprintf(stderr, "Bad number `%s'\n", buf);
+                       goto Bad;
+               }
+               if (c=='e' || c=='E') {
+                       *s++ = 'e';
+                       c = nextc();
+                       if (c == '+')
+                               c = nextc();
+                       else if (c == '-') {
+                               *s++ = c;
+                               c = nextc();
+                       }
+                       if (c<'0' || '9'<c)
+                               goto Badnumber;
+                       do {
+                               *s++ = c;
+                               c = nextc();
+                       } while('0'<=c && c<='9');
+               }
+               *s = '\0';
+               peekc = c;
+               factor = atof(buf);
+               if (div) {
+                       if (factor == 0.) {
+                               fprintf(stderr, "Divide check\n");
+                               goto Bad;
+                       }
+                       u->u_val /= factor;
+                       div = 0;
+               } else
+                       u->u_val *= factor;
+               break;
+
+       case '/':       /* divide by next unit */
+               if (div) {
+               Baddiv:
+                       fprintf(stderr, "Two division signs in a row\n");
+                       goto Bad;
+               }
+               div++;
+               break;
+
+       case '!':       /* primitive unit */
+               i = 0;
+               if ((c = nextc())<'0' || c>'9') {
+                       fprintf(stderr, "`!' must precede a number\n");
+                       goto Bad;
+               }
+               do {
+                       i = i*10+c-'0';
+                       c = nextc();
+               } while('0'<=c && c<='9');
+               peekc = c;
+               if (i<0 || NDIM<=i) {
+                       printf("Primitive unit out of range [0,%d]\n", NDIM-1);
+                       goto Bad;
+               }
+               u->u_dim[i]++;
+               break;
+
+       default:
+               s = buf;
+               do {
+                       *s++ = c;
+                       c = nextc();
+               } while(c!=EOF && !anyc(c, "/0123456789+-. \t\n"));
+               *s = '\0';
+               s = buf;
+               if (strcmp(s, "per") == 0) {
+                       if (div)
+                               goto Baddiv;
+                       div++;
+                       break;
+               }
+               if (strcmp(s, "square")==0 || strcmp(s, "sq")==0) {
+                       pow *= 2;
+                       break;
+               }
+               if (strcmp(s, "cubic")==0 || strcmp(s, "cu")==0) {
+                       pow *= 3;
+                       break;
+               }
+               factor = 1.;
+               do {
+                       for (i=0; i!=nunits; i++)
+                               if (eqplural(s, units[i].u_name))
+                                       break;
+               } while(i==nunits && (s=prefix(s, &factor))!=NULL);
+               if (i == nunits) {
+                       fprintf(stderr, "Unrecognised unit %s\n", buf);
+                       goto Bad;
+               }
+               if (c=='+' || c=='-') {
+                       if (c == '-')
+                               div = !div;
+                       expon = 0;
+                       if ((c = nextc())<'0' || c>'9') {
+                               printf("+ or - must be followed by digits\n");
+                               goto Bad;
+                       }
+                       do {
+                               expon = expon*10+c-'0';
+                               c = nextc();
+                       } while('0'<=c && c<='9');
+               } else
+                       expon = 1;
+               expon *= pow;
+               pow = 1;
+               peekc = c;
+               if (div) {
+                       expon = -expon;
+                       div = 0;
+               }
+               u->u_val *= ipow(factor*units[i].u_val, expon);
+               for (j=0; j!=NDIM; j++)
+                       u->u_dim[j] += units[i].u_dim[j]*expon;
+       }
+Bad:
+       while (c!='\n' && c!=EOF)
+               c = nextc();
+       if (prompt!=NULL)
+               goto Again;
+       printf("line %d\n", lineno);
+       return (1);
+}
+
+/*
+ * Update units information by reading the
+ * units file.
+ */
+void update(void)
+{
+       register char *name;
+       register int i;
+       register char *sstart, *send;
+       register int bfd;
+
+       fprintf(stderr, "Rebuilding %s from %s ...\n", bunamep, unamep);
+       if ((fd = fopen(unamep, "r")) == NULL)
+               err(1, "can't open unit file `%s'", ufile);
+       setbuf(fd, inbuf);
+       units = (UNIT *)alloc(NUNITS*sizeof(UNIT));
+       sstart = alloc(0);
+       for (nunits=0; nunits!=NUNITS; nunits++) {
+               name = getname();
+               for (i=0; i!=nunits; i++)
+                       if (strcmp(units[i].u_name, name) == 0)
+                               fprintf(stderr, "`%s' redefined, line %d\n",
+                                       name, lineno);
+               units[nunits].u_name = name;
+               if (!getunit(&units[nunits], NULL))
+                       break;
+       }
+       send = alloc(0);
+       if (!feof(fd))
+               errx(1, "too many units");
+       fclose(fd);
+       /*
+        * Write out, if possible, binary
+        * information for faster response next time.
+        */
+       if ((bfd = creat(bunamep, 0644)) >= 0) {
+               hdr.h_magic = UMAGIC;
+               hdr.h_nunits = nunits;
+               hdr.h_ssize = send-sstart;
+               if (write(bfd, &hdr, sizeof(hdr)) != sizeof(hdr))
+                       goto bad;
+               if (write(bfd, sstart, hdr.h_ssize) != hdr.h_ssize)
+                       goto bad;
+               for (i=0; i!=nunits; i++)
+                       units[i].u_name -= (long)sstart;/* Rel. address */
+               write(bfd, units, nunits*sizeof(UNIT));
+               for (i=0; i!=nunits; i++)
+                       units[i].u_name += (long)sstart;
+       bad:
+               close(bfd);
+       }
+}
+
+void punit(UNIT *u)
+{
+       register int i;
+
+       printf("%g", u->u_val);
+       for (i=0; i!=NDIM; i++)
+               if (u->u_dim[i] == 1)
+                       printf(" %s", units[i].u_name);
+               else if (u->u_dim[i] > 0)
+                       printf(" %s+%d", units[i].u_name, u->u_dim[i]);
+               else if (u->u_dim[i] < 0)
+                       printf(" %s-%d", units[i].u_name, -u->u_dim[i]);
+       printf("\n");
+}
+
+
+
+/*
+ * Initialise by reading in units information
+ * either in binary or ascii form and updating
+ * the binary information.
+ */
+void init(void)
+{
+       if (!binary())
+               update();
+       printf("%d units\n", nunits);
+       peekc = EOF;
+       if (uflag)
+               exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       UNIT have, want;
+       register int i;
+
+       setbuf(stdout, NULL);
+       setbuf(stderr, NULL);
+       if (argc>1 && *argv[1]=='-') {
+               if (argv[1][1]=='u' && argv[1][2]=='\0')
+                       uflag++;
+               else {
+                       fprintf(stderr, "Usage: units [-u]\n");
+                       exit(1);
+               }
+       }
+       init();
+       setbuf(fd = stdin, inbuf);
+Again:
+       if (!getunit(&have, "You have: ")
+       || !getunit(&want, "You want: "))
+               exit(0);
+       for (i=0; i!=NDIM; i++)
+               if (have.u_dim[i] != want.u_dim[i]) {
+                       printf("Conformability\n");
+                       punit(&have);
+                       punit(&want);
+                       goto Again;
+               }
+       printf("* %g\n/ %g\n", have.u_val/want.u_val, want.u_val/have.u_val);
+       goto Again;
+}
+