DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦0a889dc0e⟧ TextFile

    Length: 10034 (0x2732)
    Types: TextFile
    Notes: UNIX file
    Names: »units.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦f4b8d8c84⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cmd/units.c« 

TextFile

/*
 * 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);
}