From: Alan Cox Date: Mon, 16 May 2016 20:58:01 +0000 (+0100) Subject: units: Add units command X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=c379175c6d81b0457c955050291a9b91762b16aa;p=FUZIX.git units: Add units command Some FIXME's left, and need data files from Coherent --- diff --git a/Applications/MWC/cmd/Makefile b/Applications/MWC/cmd/Makefile index 94f58606..13480a3a 100644 --- a/Applications/MWC/cmd/Makefile +++ b/Applications/MWC/cmd/Makefile @@ -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 index 00000000..779de1fa --- /dev/null +++ b/Applications/MWC/cmd/units.c @@ -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 +#include +#include +#include +#include +#include + +#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'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; +} +