|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 10034 (0x2732)
Types: TextFile
Notes: UNIX file
Names: »units.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/units.c«
/*
* 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 <sys/stat.h>
#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[] = "/usr/lib/units";
char binufile[] = "/usr/lib/binunits";
char buf[NBUF];
char inbuf[BUFSIZ];
int nunits;
int uflag; /* Update information only */
FILE *fd;
int peekc = EOF;
int lastc = EOF;
int lineno = 1;
char *getname();
char *prefix();
char *alloc();
main(argc, argv)
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;
}
/*
* Initialise by reading in units information
* either in binary or ascii form and updating
* the binary information.
*/
init()
{
if (!binary())
update();
printf("%d units\n", nunits);
peekc = EOF;
if (uflag)
exit(0);
/*
* Throw away super-user information
*/
setuid(getuid());
}
/*
* Attempt to read in the already-stored
* binary information. Return non-zero if
* successful.
*/
binary()
{
register char *sstart;
register int n;
register int bfd;
time_t timeasc;
if (stat(ufile, &sb) < 0)
return (0);
timeasc = sb.st_mtime;
if ((bfd = open(binufile, 0))<0 || fstat(bfd, &sb)<0)
return (0);
if (timeasc > sb.st_mtime) /* Out of date? */
goto bad1;
if (read(bfd, &hdr, sizeof(hdr)) != sizeof(hdr))
goto bad1;
if (hdr.h_magic != UMAGIC)
goto bad1;
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:
brk(sstart);
bad1:
close(bfd);
return (0);
}
/*
* Update units information by reading the
* units file.
*/
update()
{
register char *name;
register int i;
register char *sstart, *send;
register int bfd;
fprintf(stderr, "Rebuilding %s from %s ...\n", binufile, ufile);
if ((fd = fopen(ufile, "r")) == NULL)
cerr("can't open unit file `%s'", ufile);
setbuf(fd, inbuf);
units = (UNIT *)alloc(NUNITS*sizeof(UNIT));
sstart = sbrk(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 = sbrk(0);
if (!feof(fd))
cerr("too many units");
fclose(fd);
/*
* Write out, if possible, binary
* information for faster response next time.
*/
if ((bfd = creat(binufile, 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);
}
}
nextc()
{
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()
{
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);
}
punit(u)
register 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");
}
double
ipow(d, n)
double d;
{
double v;
v = 1.;
if (n < 0) {
d = 1./d;
n = -n;
}
while (n) {
v *= d;
--n;
}
return (v);
}
struct{
char *prefix;
double factor;
}pre[]={
"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,
"deka", 1e1,
"hecto", 1e2,
"hekto", 1e2, /* common (?) misspelling */
"kilo", 1e3,
"myria", 1e5,
"mega", 1e6,
"giga", 1e9,
"tera", 1e12,
NULL, 1.
};
/*
* Return the string stripped of its
* prefix (if any). Set factor
* to the multiplicative factor indicated by
* the prefix found.
*/
char *
prefix(str, factor)
char *str;
double *factor;
{
register 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);
}
getunit(u, prompt)
UNIT *u;
char *prompt;
{
register int c;
register char *s;
register int i;
int j, expon, digit, div, pow;
double factor;
double atof();
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);
}
/*
* Check for any occurrences of
* the character `c' in string `s'.
*/
anyc(c, s)
register char c;
register 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'.
*/
eqplural(s, t)
register char *s, *t;
{
while (*t != '\0')
if (*s++ != *t++)
return (0);
return (*s=='\0' || (*s++=='s' && *s=='\0'));
}
/*
* Diagnostics
*/
/* VARARGS */
cerr(x)
{
fprintf(stderr, "units: %r\n", &x);
exit(1);
}
/*
* Use sbrk for alloc to get
* contiguous memory.
*/
char *
alloc(nb)
unsigned nb;
{
register char *rp;
if ((rp = sbrk(nb)) == NULL)
cerr("out of memory");
return (rp);
}