Commit Diff


commit - 3e63e5c271f7a7013dc4b3fedfb83c2d547b8c26
commit + 17e5fb8973d9e48ef53a88eb78f845f8a7b41a5b
blob - /dev/null
blob + 5eddf471283d93260b85471028533ad9e3313e17 (mode 644)
--- /dev/null
+++ src/cmd/bc.y
@@ -0,0 +1,983 @@
+%{
+	#include	<u.h>
+	#include	<libc.h>
+	#include	<bio.h>
+
+	#define	bsp_max	5000
+
+	Biobuf	*in;
+	Biobuf	stdin;
+	Biobuf	stdout;
+	char	cary[1000];
+	char*	cp = { cary };
+	char	string[1000];
+	char*	str = { string };
+	int	crs = 128;
+	int	rcrs = 128;	/* reset crs */
+	int	bindx = 0;
+	int	lev = 0;
+	int	ln;
+	int*	ttp;
+	char*	ss = "";
+	int	bstack[10] = { 0 };
+	char*	numb[15] =
+	{
+		" 0", " 1", " 2", " 3", " 4", " 5",
+		" 6", " 7", " 8", " 9", " 10", " 11",
+		" 12", " 13", " 14"
+	};
+	int*	pre;
+	int*	post;
+
+	long	peekc = -1;
+	int	sargc;
+	int	ifile;
+	char**	sargv;
+
+	char	*funtab[] =
+	{
+		"<1>","<2>","<3>","<4>","<5>",
+		"<6>","<7>","<8>","<9>","<10>",
+		"<11>","<12>","<13>","<14>","<15>",
+		"<16>","<17>","<18>","<19>","<20>",
+		"<21>","<22>","<23>","<24>","<25>",
+		"<26>"
+	};
+	char	*atab[] =
+	{
+		"<221>","<222>","<223>","<224>","<225>",
+		"<226>","<227>","<228>","<229>","<230>",
+		"<231>","<232>","<233>","<234>","<235>",
+		"<236>","<237>","<238>","<239>","<240>",
+		"<241>","<242>","<243>","<244>","<245>",
+		"<246>"
+	};
+	char*	letr[26] =
+	{
+		"a","b","c","d","e","f","g","h","i","j",
+		"k","l","m","n","o","p","q","r","s","t",
+		"u","v","w","x","y","z"
+	};
+	char*	dot = { "." };
+	int	bspace[bsp_max];
+	int*	bsp_nxt = { bspace };
+	int	bdebug = 0;
+	int	lflag;
+	int	cflag;
+	int	sflag;
+
+	int*	bundle(int, ...);
+	void	conout(int*, char*);
+	int	cpeek(int, int, int);
+	int	getch(void);
+	int*	geta(char*);
+	int*	getf(char*);
+	void	getout(void);
+	void	output(int*);
+	void	pp(char*);
+	void	routput(int*);
+	void	tp(char*);
+	void	yyerror(char*, ...);
+	int	yyparse(void);
+
+	typedef	void*	pointer;
+/*	#pragma	varargck	type	"lx"	pointer */
+
+%}
+%union
+{
+	int*	iptr;
+	char*	cptr;
+	int	cc;
+}
+
+%type	<iptr>	pstat stat stat1 def slist dlets e ase nase
+%type	<iptr>	slist re fprefix cargs eora cons constant lora
+%type	<cptr>	crs
+
+%token	<cptr>	LETTER EQOP _AUTO DOT
+%token	<cc>	DIGIT SQRT LENGTH _IF FFF EQ
+%token	<cc>	_PRINT _WHILE _FOR NE LE GE INCR DECR
+%token	<cc>	_RETURN _BREAK _DEFINE BASE OBASE SCALE
+%token	<cc>	QSTR ERROR
+
+%right	'=' EQOP
+%left	'+' '-'
+%left	'*' '/' '%'
+%right	'^'
+%left	UMINUS
+
+%%
+start:
+	start stuff
+|	stuff
+
+stuff:
+	pstat tail
+	{
+		output($1);
+	}
+|	def dargs ')' '{' dlist slist '}'
+	{
+		ttp = bundle(6, pre, $6, post , "0", numb[lev], "Q");
+		conout(ttp, (char*)$1);
+		rcrs = crs;
+		output((int*)"");	/* this is horse puk!! */
+		lev = bindx = 0;
+	}
+
+dlist:
+	tail
+|	dlist _AUTO dlets tail
+
+stat:
+	stat1
+|	nase
+	{
+		if(sflag)
+			bundle(2, $1, "s.");
+	}
+
+pstat:
+	stat1
+	{
+		if(sflag)
+			bundle(2, $1, "0");
+	}
+|	nase
+	{
+		if(!sflag)
+			bundle(2, $1, "ps.");
+	}
+
+stat1:
+	{
+		bundle(1, "");
+	}
+|	ase
+	{
+		bundle(2, $1, "s.");
+	}
+|	SCALE '=' e
+	{
+		bundle(2, $3, "k");
+	}
+|	SCALE EQOP e
+	{
+		bundle(4, "K", $3, $2, "k");
+	}
+|	BASE '=' e
+	{
+		bundle(2, $3, "i");
+	}
+|	BASE EQOP e
+	{
+		bundle(4, "I", $3, $2, "i");
+	}
+|	OBASE '=' e
+	{
+		bundle(2, $3, "o");
+	}
+|	OBASE EQOP e
+	{
+		bundle(4, "O", $3, $2, "o");
+	}
+|	QSTR
+	{
+		bundle(3, "[", $1, "]P");
+	}
+|	_BREAK
+	{
+		bundle(2, numb[lev-bstack[bindx-1]], "Q");
+	}
+|	_PRINT e
+	{
+		bundle(2, $2, "ps.");
+	}
+|	_RETURN e
+	{
+		bundle(4, $2, post, numb[lev], "Q");
+	}
+|	_RETURN
+	{
+		bundle(4, "0", post, numb[lev], "Q");
+	}
+|	'{' slist '}'
+	{
+		$$ = $2;
+	}
+|	FFF
+	{
+		bundle(1, "fY");
+	}
+|	_IF crs BLEV '(' re ')' stat
+	{
+		conout($7, $2);
+		bundle(3, $5, $2, " ");
+	}
+|	_WHILE crs '(' re ')' stat BLEV
+	{
+		bundle(3, $6, $4, $2);
+		conout($$, $2);
+		bundle(3, $4, $2, " ");
+	}
+|	fprefix crs re ';' e ')' stat BLEV
+	{
+		bundle(5, $7, $5, "s.", $3, $2);
+		conout($$, $2);
+		bundle(5, $1, "s.", $3, $2, " ");
+	}
+|	'~' LETTER '=' e
+	{
+		bundle(3, $4, "S", $2);
+	}
+
+fprefix:
+	_FOR '(' e ';'
+	{
+		$$ = $3;
+	}
+
+BLEV:
+	=
+	{
+		--bindx;
+	}
+
+slist:
+	stat
+|	slist tail stat
+	{
+		bundle(2, $1, $3);
+	}
+
+tail:
+	'\n'
+	{
+		ln++;
+	}
+|	';'
+
+re:
+	e EQ e
+	{
+		$$ = bundle(3, $1, $3, "=");
+	}
+|	e '<' e
+	{
+		bundle(3, $1, $3, ">");
+	}
+|	e '>' e
+	{
+		bundle(3, $1, $3, "<");
+	}
+|	e NE e
+	{
+		bundle(3, $1, $3, "!=");
+	}
+|	e GE e
+	{
+		bundle(3, $1, $3, "!>");
+	}
+|	e LE e
+	{
+		bundle(3, $1, $3, "!<");
+	}
+|	e
+	{
+		bundle(2, $1, " 0!=");
+	}
+
+nase:
+	'(' e ')'
+	{
+		$$ = $2;
+	}
+|	cons
+	{
+		bundle(3, " ", $1, " ");
+	}
+|	DOT cons
+	{
+		bundle(3, " .", $2, " ");
+	}
+|	cons DOT cons
+	{
+		bundle(5, " ", $1, ".", $3, " ");
+	}
+|	cons DOT
+	{
+		bundle(4, " ", $1, ".", " ");
+	}
+|	DOT
+	{
+		$<cptr>$ = "l.";
+	}
+|	LETTER '[' e ']'
+	{
+		bundle(3, $3, ";", geta($1));
+	}
+|	LETTER INCR
+	{
+		bundle(4, "l", $1, "d1+s", $1);
+	}
+|	INCR LETTER
+	{
+		bundle(4, "l", $2, "1+ds", $2);
+	}
+|	DECR LETTER
+	{
+		bundle(4, "l", $2, "1-ds", $2);
+	}
+|	LETTER DECR
+	{
+		bundle(4, "l", $1, "d1-s", $1);
+	}
+|	LETTER '[' e ']' INCR
+	{
+		bundle(7, $3, ";", geta($1), "d1+" ,$3, ":" ,geta($1));
+	}
+|	INCR LETTER '[' e ']'
+	{
+		bundle(7, $4, ";", geta($2), "1+d", $4, ":", geta($2));
+	}
+|	LETTER '[' e ']' DECR
+	{
+		bundle(7, $3, ";", geta($1), "d1-", $3, ":", geta($1));
+	}
+|	DECR LETTER '[' e ']'
+	{
+		bundle(7, $4, ";", geta($2), "1-d", $4, ":" ,geta($2));
+	}
+|	SCALE INCR
+	{
+		bundle(1, "Kd1+k");
+	}
+|	INCR SCALE
+	{
+		bundle(1, "K1+dk");
+	}
+|	SCALE DECR
+	{
+		bundle(1, "Kd1-k");
+	}
+|	DECR SCALE
+	{
+		bundle(1, "K1-dk");
+	}
+|	BASE INCR
+	{
+		bundle(1, "Id1+i");
+	}
+|	INCR BASE
+	{
+		bundle(1, "I1+di");
+	}
+|	BASE DECR
+	{
+		bundle(1, "Id1-i");
+	}
+|	DECR BASE
+	{
+		bundle(1, "I1-di");
+	}
+|	OBASE INCR
+	{
+		bundle(1, "Od1+o");
+	}
+|	INCR OBASE
+	{
+		bundle(1, "O1+do");
+	}
+|	OBASE DECR
+	{
+		bundle(1, "Od1-o");
+	}
+|	DECR OBASE
+	{
+		bundle(1, "O1-do");
+	}
+|	LETTER '(' cargs ')'
+	{
+		bundle(4, $3, "l", getf($1), "x");
+	}
+|	LETTER '(' ')'
+	{
+		bundle(3, "l", getf($1), "x");
+	}
+|	LETTER = {
+		bundle(2, "l", $1);
+	}
+|	LENGTH '(' e ')'
+	{
+		bundle(2, $3, "Z");
+	}
+|	SCALE '(' e ')'
+	{
+		bundle(2, $3, "X");
+	}
+|	'?'
+	{
+		bundle(1, "?");
+	}
+|	SQRT '(' e ')'
+	{
+		bundle(2, $3, "v");
+	}
+|	'~' LETTER
+	{
+		bundle(2, "L", $2);
+	}
+|	SCALE
+	{
+		bundle(1, "K");
+	}
+|	BASE
+	{
+		bundle(1, "I");
+	}
+|	OBASE
+	{
+		bundle(1, "O");
+	}
+|	'-' e
+	{
+		bundle(3, " 0", $2, "-");
+	}
+|	e '+' e
+	{
+		bundle(3, $1, $3, "+");
+	}
+|	e '-' e
+	{
+		bundle(3, $1, $3, "-");
+	}
+|	e '*' e
+	{
+		bundle(3, $1, $3, "*");
+	}
+|	e '/' e
+	{
+		bundle(3, $1, $3, "/");
+	}
+|	e '%' e
+	{
+		bundle(3, $1, $3, "%%");
+	}
+|	e '^' e
+	{
+		bundle(3, $1, $3, "^");
+	}
+
+ase:
+	LETTER '=' e
+	{
+		bundle(3, $3, "ds", $1);
+	}
+|	LETTER '[' e ']' '=' e
+	{
+		bundle(5, $6, "d", $3, ":", geta($1));
+	}
+|	LETTER EQOP e
+	{
+		bundle(6, "l", $1, $3, $2, "ds", $1);
+	}
+|	LETTER '[' e ']' EQOP e
+	{
+		bundle(9, $3, ";", geta($1), $6, $5, "d", $3, ":", geta($1));
+	}
+
+e:
+	ase
+|	nase
+
+cargs:
+	eora
+|	cargs ',' eora
+	{
+		bundle(2, $1, $3);
+	}
+
+eora:
+	e
+|	LETTER '[' ']'
+	{
+		bundle(2, "l", geta($1));
+	}
+
+cons:
+	constant
+	{
+		*cp++ = 0;
+	}
+
+constant:
+	'_'
+	{
+		$<cptr>$ = cp;
+		*cp++ = '_';
+	}
+|	DIGIT
+	{
+		$<cptr>$ = cp;
+		*cp++ = $1;
+	}
+|	constant DIGIT
+	{
+		*cp++ = $2;
+	}
+
+crs:
+	=
+	{
+		$$ = cp;
+		*cp++ = '<';
+		*cp++ = crs/100+'0';
+		*cp++ = (crs%100)/10+'0';
+		*cp++ = crs%10+'0';
+		*cp++ = '>';
+		*cp++ = '\0';
+		if(crs++ >= 220) {
+			yyerror("program too big");
+			getout();
+		}
+		bstack[bindx++] = lev++;
+	}
+
+def:
+	_DEFINE LETTER '('
+	{
+		$$ = getf($2);
+		pre = (int*)"";
+		post = (int*)"";
+		lev = 1;
+		bindx = 0;
+		bstack[bindx] = 0;
+	}
+
+dargs:
+|	lora
+	{
+		pp((char*)$1);
+	}
+|	dargs ',' lora
+	{
+		pp((char*)$3);
+	}
+
+dlets:
+	lora
+	{
+		tp((char*)$1);
+	}
+|	dlets ',' lora
+	{
+		tp((char*)$3);
+	}
+
+lora:
+	LETTER
+	{
+		$<cptr>$=$1;
+	}
+|	LETTER '[' ']'
+	{
+		$$ = geta($1);
+	}
+
+%%
+
+int
+yylex(void)
+{
+	int c, ch;
+
+restart:
+	c = getch();
+	peekc = -1;
+	while(c == ' ' || c == '\t')
+		c = getch();
+	if(c == '\\') {
+		getch();
+		goto restart;
+	}
+	if(c >= 'a' && c <= 'z') {
+		/* look ahead to look for reserved words */
+		peekc = getch();
+		if(peekc >= 'a' && peekc <= 'z') { /* must be reserved word */
+			if(c=='p' && peekc=='r') {
+				c = _PRINT;
+				goto skip;
+			}
+			if(c=='i' && peekc=='f') {
+				c = _IF;
+				goto skip;
+			}
+			if(c=='w' && peekc=='h') {
+				c = _WHILE;
+				goto skip;
+			}
+			if(c=='f' && peekc=='o') {
+				c = _FOR;
+				goto skip;
+			}
+			if(c=='s' && peekc=='q') {
+				c = SQRT;
+				goto skip;
+			}
+			if(c=='r' && peekc=='e') {
+				c = _RETURN;
+				goto skip;
+			}
+			if(c=='b' && peekc=='r') {
+				c = _BREAK;
+				goto skip;
+			}
+			if(c=='d' && peekc=='e') {
+				c = _DEFINE;
+				goto skip;
+			}
+			if(c=='s' && peekc=='c') {
+				c = SCALE;
+				goto skip;
+			}
+			if(c=='b' && peekc=='a') {
+				c = BASE;
+				goto skip;
+			}
+			if(c=='i' && peekc=='b') {
+				c = BASE;
+				goto skip;
+			}
+			if(c=='o' && peekc=='b') {
+				c = OBASE;
+				goto skip;
+			}
+			if(c=='d' && peekc=='i') {
+				c = FFF;
+				goto skip;
+			}
+			if(c=='a' && peekc=='u') {
+				c = _AUTO;
+				goto skip;
+			}
+			if(c=='l' && peekc=='e') {
+				c = LENGTH;
+				goto skip;
+			}
+			if(c=='q' && peekc=='u')
+				getout();
+			/* could not be found */
+			return ERROR;
+
+		skip:	/* skip over rest of word */
+			peekc = -1;
+			for(;;) {
+				ch = getch();
+				if(ch < 'a' || ch > 'z')
+					break;
+			}
+			peekc = ch;
+			return c;
+		}
+
+		/* usual case; just one single letter */
+		yylval.cptr = letr[c-'a'];
+		return LETTER;
+	}
+	if((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
+		yylval.cc = c;
+		return DIGIT;
+	}
+	switch(c) {
+	case '.':
+		return DOT;
+	case '*':
+		yylval.cptr = "*";
+		return cpeek('=', EQOP, c);
+	case '%':
+		yylval.cptr = "%%";
+		return cpeek('=', EQOP, c);
+	case '^':
+		yylval.cptr = "^";
+		return cpeek('=', EQOP, c);
+	case '+':
+		ch = cpeek('=', EQOP, c);
+		if(ch == EQOP) {
+			yylval.cptr = "+";
+			return ch;
+		}
+		return cpeek('+', INCR, c);
+	case '-':
+		ch = cpeek('=', EQOP, c);
+		if(ch == EQOP) {
+			yylval.cptr = "-";
+			return ch;
+		}
+		return cpeek('-', DECR, c);
+	case '=':
+		return cpeek('=', EQ, '=');
+	case '<':
+		return cpeek('=', LE, '<');
+	case '>':
+		return cpeek('=', GE, '>');
+	case '!':
+		return cpeek('=', NE, '!');
+	case '/':
+		ch = cpeek('=', EQOP, c);
+		if(ch == EQOP) {
+			yylval.cptr = "/";
+			return ch;
+		}
+		if(peekc == '*') {
+			peekc = -1;
+			for(;;) {
+				ch = getch();
+				if(ch == '*') {
+					peekc = getch();
+					if(peekc == '/') {
+						peekc = -1;
+						goto restart;
+					}
+				}
+			}
+		}
+		return c;
+	case '"':
+		yylval.cptr = str;
+		while((c=getch()) != '"'){
+			*str++ = c;
+			if(str >= &string[999]){
+				yyerror("string space exceeded");
+				getout();
+			}
+		}
+		*str++ = 0;
+		return QSTR;
+	default:
+		return c;
+	}
+}
+
+int
+cpeek(int c, int yes, int no)
+{
+
+	peekc = getch();
+	if(peekc == c) {
+		peekc = -1;
+		return yes;
+	}
+	return no;
+}
+
+int
+getch(void)
+{
+	long ch;
+
+loop:
+	ch = peekc;
+	if(ch < 0){
+		if(in == 0)
+			ch = -1;
+		else
+			ch = Bgetc(in);
+	}
+	peekc = -1;
+	if(ch >= 0)
+		return ch;
+	ifile++;
+	if(ifile > sargc) {
+		if(ifile >= sargc+2)
+			getout();
+		in = &stdin;
+		Binit(in, 0, OREAD);
+		ln = 0;
+		goto loop;
+	}
+	Bterm(in);
+	if((in = Bopen(sargv[ifile], OREAD)) != 0){
+		ln = 0;
+		ss = sargv[ifile];
+		goto loop;
+	}
+	yyerror("cannot open input file");
+	return 0;		/* shut up ken */
+}
+
+int*
+bundle(int a, ...)
+{
+	int i, *p, *q;
+
+	p = &a;
+	i = *p++;
+	q = bsp_nxt;
+	if(bdebug)
+		fprint(2, "bundle %d elements at %lx\n", i, q);
+	while(i-- > 0) {
+		if(bsp_nxt >= &bspace[bsp_max])
+			yyerror("bundling space exceeded");
+		*bsp_nxt++ = *p++;
+	}
+	*bsp_nxt++ = 0;
+	yyval.iptr = q;
+	return q;
+}
+
+void
+routput(int *p)
+{
+	if(bdebug)
+		fprint(2, "routput(%lx)\n", p);
+	if(p >= &bspace[0] && p < &bspace[bsp_max]) {
+		/* part of a bundle */
+		while(*p != 0)
+			routput((int*)(*p++));
+	} else
+		Bprint(&stdout, (char*)p);	/* character string */
+}
+
+void
+output(int *p)
+{
+	routput(p);
+	bsp_nxt = &bspace[0];
+	Bprint(&stdout, "\n");
+	Bflush(&stdout);
+	cp = cary;
+	crs = rcrs;
+}
+
+void
+conout(int *p, char *s)
+{
+	Bprint(&stdout, "[");
+	routput(p);
+	Bprint(&stdout, "]s%s\n", s);
+	Bflush(&stdout);
+	lev--;
+}
+
+void
+yyerror(char *s, ...)
+{
+	if(ifile > sargc)
+		ss = "teletype";
+	Bprint(&stdout, "c[%s on line %d, %s]pc\n", s, ln+1, ss);
+	Bflush(&stdout);
+	cp = cary;
+	crs = rcrs;
+	bindx = 0;
+	lev = 0;
+	bsp_nxt = &bspace[0];
+}
+
+void
+pp(char *s)
+{
+	/* puts the relevant stuff on pre and post for the letter s */
+	bundle(3, "S", s, pre);
+	pre = yyval.iptr;
+	bundle(4, post, "L", s, "s.");
+	post = yyval.iptr;
+}
+
+void
+tp(char *s)
+{
+	/* same as pp, but for temps */
+	bundle(3, "0S", s, pre);
+	pre = yyval.iptr;
+	bundle(4, post, "L", s, "s.");
+	post = yyval.iptr;
+}
+
+void
+yyinit(int argc, char **argv)
+{
+	Binit(&stdout, 1, OWRITE);
+	sargv = argv;
+	sargc = argc - 1;
+	if(sargc == 0) {
+		in = &stdin;
+		Binit(in, 0, OREAD);
+	} else if((in = Bopen(sargv[1], OREAD)) == 0)
+		yyerror("cannot open input file");
+	ifile = 1;
+	ln = 0;
+	ss = sargv[1];
+}
+
+void
+getout(void)
+{
+	Bprint(&stdout, "q");
+	Bflush(&stdout);
+	exits(0);
+}
+
+int*
+getf(char *p)
+{
+	return (int*)funtab[*p - 'a'];
+}
+
+int*
+geta(char *p)
+{
+	return (int*)atab[*p - 'a'];
+}
+
+void
+main(int argc, char **argv)
+{
+	int p[2];
+
+	while(argc > 1 && *argv[1] == '-') {
+		switch(argv[1][1]) {
+		case 'd':
+			bdebug++;
+			break;
+		case 'c':
+			cflag++;
+			break;
+		case 'l':
+			lflag++;
+			break;
+		case 's':
+			sflag++;
+			break;
+		default:
+			fprint(2, "Usage: bc [-l] [-c] [file ...]\n");
+			exits("usage");
+		}
+		argc--;
+		argv++;
+	}
+	if(lflag) {
+		argv--;
+		argc++;
+		argv[1] = "/sys/lib/bclib";
+	}
+	if(cflag) {
+		yyinit(argc, argv);
+		for(;;)
+			yyparse();
+		exits(0);
+	}
+	pipe(p);
+	if(fork() == 0) {
+		dup(p[1], 1);
+		close(p[0]);
+		close(p[1]);
+		yyinit(argc, argv);
+		for(;;)
+			yyparse();
+	}
+	dup(p[0], 0);
+	close(p[0]);
+	close(p[1]);
+	execl("/bin/dc", "dc", 0);
+}
blob - 198f8e2fb1a4ca1f9d851337f569d63c8df7f860
blob + cdefe4139fbcff23d55dd45b935e1da54c11e0ea
--- src/cmd/jpg/mkfile
+++ src/cmd/jpg/mkfile
@@ -38,12 +38,12 @@ torgbv.$O:	ycbcr.h rgbv.h
 
 ycbcr.h:	rgbycc.c
 	9c rgbycc.c
-	9l -o o.rgbycc rgbycc.c
+	9l -o o.rgbycc rgbycc.o -ldraw -l9
 	./o.rgbycc >ycbcr.h
 
 rgbv.h:	rgbrgbv.c
 	9c rgbrgbv.c
-	9l -o o.rgbrgbv rgbrgbv.c
+	9l -o o.rgbrgbv rgbrgbv.o -ldraw -l9
 	./o.rgbrgbv >rgbv.h
 
 nuke:V:	nuke-headers
blob - 1b064ebc1600981a6c8f4fecdb9043b4cbc60e67
blob + ab348260c3a49042758f9f39e767bf3d3ab39a39
--- src/cmd/mkfile
+++ src/cmd/mkfile
@@ -1,8 +1,7 @@
 <$PLAN9/src/mkhdr
 
-TARG=`ls *.c | sed 's/\.c//'`
-LDFLAGS=$LDFLAGS -L$X11/lib -lX11
-SHORTLIB=sec fs mux regexp9 draw thread bio 9
+TARG=`ls *.[cy] | sed 's/\.c//'`
+SHORTLIB=sec fs mux regexp9 thread bio 9
 
 <$PLAN9/src/mkmany
 
@@ -11,4 +10,21 @@ DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "^($BUGGERE
 
 <$PLAN9/src/mkdirs
 
-dir-install: $PLAN9/bin/yacc
+dir-all dir-install: $PLAN9/bin/9yacc
+
+XLIB=draw bio 9
+$O.clock: clock.$O ${XLIB:%=$PLAN9/lib/lib%.a}
+	$LD -o $target $prereq -L$X11/lib -lX11
+
+$O.tweak: tweak.$O ${XLIB:%=$PLAN9/lib/lib%.a}
+	$LD -o $target $prereq -L$X11/lib -lX11
+
+%.tab.h %.tab.c: %.y
+	$YACC $YFLAGS -s $stem $prereq
+
+%.o: %.tab.c
+	9c -o $target $stem.tab.c
+
+CLEANFILES=$CLEANFILES bc.tab.[ch] units.tab.[ch]
+
+
blob - /dev/null
blob + db5ea49078a96a04b40e871c32b4db8c62827729 (mode 644)
--- /dev/null
+++ src/cmd/mtime.c
@@ -0,0 +1,33 @@
+#include <u.h>
+#include <libc.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: mtime file...\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int errors, i;
+	Dir *d;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+
+	errors = 0;
+	for(i=0; i<argc; i++){
+		if((d = dirstat(argv[i])) == nil){
+			fprint(2, "stat %s: %r\n", argv[i]);
+			errors = 1;
+		}else{
+			print("%11lud %s\n", d->mtime, argv[i]);
+			free(d);
+		}
+	}
+	exits(errors ? "errors" : nil);
+}
blob - /dev/null
blob + c6a99a30e90f9e6511151778c2e39cd13e3821a0 (mode 644)
--- /dev/null
+++ src/cmd/news.c
@@ -0,0 +1,231 @@
+/*
+ *	news foo	prints /lib/news/foo
+ *	news -a		prints all news items, latest first
+ *	news -n		lists names of new items
+ *	news		prints items changed since last news
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define	NINC	50	/* Multiples of directory allocation */
+char	*NEWS = "#9/news";
+char	TFILE[] = "%s/lib/newstime";
+
+/*
+ *	The following items should not be printed.
+ */
+char*	ignore[] =
+{
+	"core",
+	"dead.letter",
+	0
+};
+
+typedef
+struct
+{
+	long	time;
+	char	*name;
+	vlong	length;
+} File;
+File*	n_list;
+int	n_count;
+int	n_items;
+Biobuf	bout;
+
+int	fcmp(const void *a, const void *b);
+void	read_dir(int update);
+void	print_item(char *f);
+void	eachitem(void (*emit)(char*), int all, int update);
+void	note(char *s);
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+
+	NEWS = unsharp(NEWS);
+
+	Binit(&bout, 1, OWRITE);
+	if(argc == 1) {
+		eachitem(print_item, 0, 1);
+		exits(0);
+	}
+	ARGBEGIN{
+	case 'a':	/* print all */
+		eachitem(print_item, 1, 0);
+		break;
+
+	case 'n':	/* names only */
+		eachitem(note, 0, 0);
+		if(n_items)
+			Bputc(&bout, '\n');
+		break;
+
+	default:
+		fprint(2, "news: bad option %c\n", ARGC());
+		exits("usage");
+	}ARGEND
+	for(i=0; i<argc; i++)
+		print_item(argv[i]);
+	exits(0);
+}
+
+int
+fcmp(const void *a, const void *b)
+{
+	long x;
+
+	x = ((File*)b)->time - ((File*)a)->time;
+	if(x < 0)
+		return -1;
+	if(x > 0)
+		return 1;
+	return 0;
+}
+
+/*
+ *	read_dir: get the file names and modification dates for the
+ *	files in /usr/news into n_list; sort them in reverse by
+ *	modification date.
+ */
+void
+read_dir(int update)
+{
+	Dir *d;
+	char newstime[100], *home;
+	int i, j, n, na, fd;
+
+	n_count = 0;
+	n_list = malloc(NINC*sizeof(File));
+	na = NINC;
+	home = getenv("home");
+	if(home) {
+		sprint(newstime, TFILE, home);
+		d = dirstat(newstime);
+		if(d != nil) {
+			n_list[n_count].name = strdup("");
+			n_list[n_count].time =d->mtime-1;
+			n_list[n_count].length = 0;
+			n_count++;
+			free(d);
+		}
+		if(update) {
+			fd = create(newstime, OWRITE, 0644);
+			if(fd >= 0)
+				close(fd);
+		}
+	}
+	fd = open(NEWS, OREAD);
+	if(fd < 0) {
+		fprint(2, "news: ");
+		perror(NEWS);
+		exits(NEWS);
+	}
+
+	n = dirreadall(fd, &d);
+	for(i=0; i<n; i++) {
+		for(j=0; ignore[j]; j++)
+			if(strcmp(ignore[j], d[i].name) == 0)
+				goto ign;
+		if(na <= n_count) {
+			na += NINC;
+			n_list = realloc(n_list, na*sizeof(File));
+		}
+		n_list[n_count].name = strdup(d[i].name);
+		n_list[n_count].time = d[i].mtime;
+		n_list[n_count].length = d[i].length;
+		n_count++;
+	ign:;
+	}
+	free(d);
+
+	close(fd);
+	qsort(n_list, n_count, sizeof(File), fcmp);
+}
+
+void
+print_item(char *file)
+{
+	char name[4096], *p, *ep;
+	Dir *dbuf;
+	int f, c;
+	int bol, bop;
+
+	sprint(name, "%s/%s", NEWS, file);
+	f = open(name, OREAD);
+	if(f < 0) {
+		fprint(2, "news: ");
+		perror(name);
+		return;
+	}
+	strcpy(name, "...");
+	dbuf = dirfstat(f);
+	if(dbuf == nil)
+		return;
+	Bprint(&bout, "\n%s (%s) %s\n", file,
+		dbuf->muid[0]? dbuf->muid : dbuf->uid,
+		asctime(localtime(dbuf->mtime)));
+	free(dbuf);
+
+	bol = 1;	/* beginning of line ...\n */
+	bop = 1;	/* beginning of page ...\n\n */
+	for(;;) {
+		c = read(f, name, sizeof(name));
+		if(c <= 0)
+			break;
+		p = name;
+		ep = p+c;
+		while(p < ep) {
+			c = *p++;
+			if(c == '\n') {
+				if(!bop) {
+					Bputc(&bout, c);
+					if(bol)
+						bop = 1;
+					bol = 1;
+				}
+				continue;
+			}
+			if(bol) {
+				Bputc(&bout, '\t');
+				bol = 0;
+				bop = 0;
+			}
+			Bputc(&bout, c);
+		}
+	}
+	if(!bol)
+		Bputc(&bout, '\n');
+	close(f);
+}
+
+void
+eachitem(void (*emit)(char*), int all, int update)
+{
+	int i;
+
+	read_dir(update);
+	for(i=0; i<n_count; i++) {
+		if(n_list[i].name[0] == 0) {	/* newstime */
+			if(all)
+				continue;
+			break;
+		}
+		if(n_list[i].length == 0)		/* in progress */
+			continue;
+		(*emit)(n_list[i].name);
+	}
+}
+
+void
+note(char *file)
+{
+
+	if(!n_items)
+		Bprint(&bout, "news:");
+	Bprint(&bout, " %s", file);
+	n_items++;
+}
blob - /dev/null
blob + 0e926545ff3c7c8dcb60ed3d57a2eee45fd7a1cd (mode 644)
--- /dev/null
+++ src/cmd/primes.c
@@ -0,0 +1,131 @@
+#include	<u.h>
+#include	<libc.h>
+
+#define	ptsiz	(sizeof(pt)/sizeof(pt[0]))
+#define	whsiz	(sizeof(wheel)/sizeof(wheel[0]))
+#define	tabsiz	(sizeof(table)/sizeof(table[0]))
+#define	tsiz8	(tabsiz*8)
+
+double	big = 9.007199254740992e15;
+
+int	pt[] =
+{
+	  2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
+	 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+	 73, 79, 83, 89, 97,101,103,107,109,113,
+	127,131,137,139,149,151,157,163,167,173,
+	179,181,191,193,197,199,211,223,227,229,
+};
+double	wheel[] =
+{
+	10, 2, 4, 2, 4, 6, 2, 6, 4, 2,
+	 4, 6, 6, 2, 6, 4, 2, 6, 4, 6,
+	 8, 4, 2, 4, 2, 4, 8, 6, 4, 6,
+	 2, 4, 6, 2, 6, 6, 4, 2, 4, 6,
+	 2, 6, 4, 2, 4, 2,10, 2,
+};
+uchar	table[1000];
+uchar	bittab[] =
+{
+	1, 2, 4, 8, 16, 32, 64, 128,
+};
+
+void	mark(double nn, long k);
+void	ouch(void);
+
+void
+main(int argc, char *argp[])
+{
+	int i;
+	double k, temp, v, limit, nn;
+
+	if(argc <= 1) {
+		fprint(2, "usage: primes starting [ending]\n");
+		exits("usage");
+	}
+	nn = atof(argp[1]);
+	limit = big;
+	if(argc > 2) {
+		limit = atof(argp[2]);
+		if(limit < nn)
+			exits(0);
+		if(limit > big)
+			ouch();
+	}
+	if(nn < 0 || nn > big)
+		ouch();
+	if(nn == 0)
+		nn = 1;
+
+	if(nn < 230) {
+		for(i=0; i<ptsiz; i++) {
+			if(pt[i] < nn)
+				continue;
+			if(pt[i] > limit)
+				exits(0);
+			print("%d\n", pt[i]);
+			if(limit >= big)
+				exits(0);
+		}
+		nn = 230;
+	}
+
+	modf(nn/2, &temp);
+	nn = 2.*temp + 1;
+/*
+ *	clear the sieve table.
+ */
+	for(;;) {
+		for(i=0; i<tabsiz; i++)
+			table[i] = 0;
+/*
+ *	run the sieve.
+ */
+		v = sqrt(nn+tsiz8);
+		mark(nn, 3);
+		mark(nn, 5);
+		mark(nn, 7);
+		for(i=0,k=11; k<=v; k+=wheel[i]) {
+			mark(nn, k);
+			i++;
+			if(i >= whsiz)
+				i = 0;
+		}
+/*
+ *	now get the primes from the table
+ *	and print them.
+ */
+		for(i=0; i<tsiz8; i+=2) {
+			if(table[i>>3] & bittab[i&07])
+				continue;
+			temp = nn + i;
+			if(temp > limit)
+				exits(0);
+			print("%.0f\n", temp);
+			if(limit >= big)
+				exits(0);
+		}
+		nn += tsiz8;
+	}
+}
+
+void
+mark(double nn, long k)
+{
+	double t1;
+	long j;
+
+	modf(nn/k, &t1);
+	j = k*t1 - nn;
+	if(j < 0)
+		j += k;
+	for(; j<tsiz8; j+=k)
+		table[j>>3] |= bittab[j&07];
+}
+
+void
+ouch(void)
+{
+	fprint(2, "limits exceeded\n");
+	exits("limits");
+}
blob - /dev/null
blob + 372295fd2bee0f199a66c38ccb3df13b5960ad5f (mode 644)
--- /dev/null
+++ src/cmd/units.y
@@ -0,0 +1,795 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+enum
+{
+	Ndim	= 15,		/* number of dimensions */
+	Nsym	= 40,		/* size of a name */
+	Nvar	= 203,		/* hash table size */
+	Maxe	= 695,		/* log of largest number */
+};
+
+typedef	struct	Var	Var;
+typedef	struct	Node	Node;
+typedef	struct	Prefix	Prefix;
+
+struct	Node
+{
+	double	val;
+	schar	dim[Ndim];
+};
+struct	Var
+{
+	Rune	name[Nsym];
+	Node	node;
+	Var*	link;
+};
+struct	Prefix
+{
+	double	val;
+	char*	name;
+	Rune*	pname;
+};
+
+char	buf[100];
+int	digval;
+Biobuf*	fi;
+Biobuf	linebuf;
+Var*	fund[Ndim];
+Rune	line[1000];
+ulong	lineno;
+int	linep;
+int	nerrors;
+Node	one;
+int	peekrune;
+Node	retnode1;
+Node	retnode2;
+Node	retnode;
+Rune	sym[Nsym];
+Var*	vars[Nvar];
+int	vflag;
+
+#define div unitsdiv
+
+extern	void	add(Node*, Node*, Node*);
+extern	void	div(Node*, Node*, Node*);
+extern	int	specialcase(Node*, Node*, Node*);
+extern	double	fadd(double, double);
+extern	double	fdiv(double, double);
+extern	double	fmul(double, double);
+extern	int	gdigit(void*);
+extern	Var*	lookup(int);
+extern	void	main(int, char*[]);
+extern	void	mul(Node*, Node*, Node*);
+extern	void	ofile(void);
+extern	double	pname(void);
+extern	void	printdim(char*, int, int);
+extern	int	ralpha(int);
+extern	int	readline(void);
+extern	void	sub(Node*, Node*, Node*);
+extern	int	Ufmt(Fmt*);
+extern	void	xpn(Node*, Node*, int);
+extern	void	yyerror(char*, ...);
+extern	int	yylex(void);
+extern	int	yyparse(void);
+
+typedef	Node*	indnode;
+/* #pragma	varargck	type	"U"	indnode */
+
+%}
+%union
+{
+	Node	node;
+	Var*	var;
+	int	numb;
+	double	val;
+}
+
+%type	<node>	prog expr expr0 expr1 expr2 expr3 expr4
+
+%token	<val>	VAL
+%token	<var>	VAR
+%token	<numb>	SUP
+%%
+prog:
+	':' VAR expr
+	{
+		int f;
+
+		f = $2->node.dim[0];
+		$2->node = $3;
+		$2->node.dim[0] = 1;
+		if(f)
+			yyerror("redefinition of %S", $2->name);
+		else
+		if(vflag)
+			print("%S\t%U\n", $2->name, &$2->node);
+	}
+|	':' VAR '#'
+	{
+		int f, i;
+
+		for(i=1; i<Ndim; i++)
+			if(fund[i] == 0)
+				break;
+		if(i >= Ndim) {
+			yyerror("too many dimensions");
+			i = Ndim-1;
+		}
+		fund[i] = $2;
+
+		f = $2->node.dim[0];
+		$2->node = one;
+		$2->node.dim[0] = 1;
+		$2->node.dim[i] = 1;
+		if(f)
+			yyerror("redefinition of %S", $2->name);
+		else
+		if(vflag)
+			print("%S\t#\n", $2->name);
+	}
+|	'?' expr
+	{
+		retnode1 = $2;
+	}
+|	'?'
+	{
+		retnode1 = one;
+	}
+
+expr:
+	expr4
+|	expr '+' expr4
+	{
+		add(&$$, &$1, &$3);
+	}
+|	expr '-' expr4
+	{
+		sub(&$$, &$1, &$3);
+	}
+
+expr4:
+	expr3
+|	expr4 '*' expr3
+	{
+		mul(&$$, &$1, &$3);
+	}
+|	expr4 '/' expr3
+	{
+		div(&$$, &$1, &$3);
+	}
+
+expr3:
+	expr2
+|	expr3 expr2
+	{
+		mul(&$$, &$1, &$2);
+	}
+
+expr2:
+	expr1
+|	expr2 SUP
+	{
+		xpn(&$$, &$1, $2);
+	}
+|	expr2 '^' expr1
+	{
+		int i;
+
+		for(i=1; i<Ndim; i++)
+			if($3.dim[i]) {
+				yyerror("exponent has units");
+				$$ = $1;
+				break;
+			}
+		if(i >= Ndim) {
+			i = $3.val;
+			if(i != $3.val)
+				yyerror("exponent not integral");
+			xpn(&$$, &$1, i);
+		}
+	}
+
+expr1:
+	expr0
+|	expr1 '|' expr0
+	{
+		div(&$$, &$1, &$3);
+	}
+
+expr0:
+	VAR
+	{
+		if($1->node.dim[0] == 0) {
+			yyerror("undefined %S", $1->name);
+			$$ = one;
+		} else
+			$$ = $1->node;
+	}
+|	VAL
+	{
+		$$ = one;
+		$$.val = $1;
+	}
+|	'(' expr ')'
+	{
+		$$ = $2;
+	}
+%%
+
+int
+yylex(void)
+{
+	int c, i;
+
+	c = peekrune;
+	peekrune = ' ';
+
+loop:
+	if((c >= '0' && c <= '9') || c == '.')
+		goto numb;
+	if(ralpha(c))
+		goto alpha;
+	switch(c) {
+	case ' ':
+	case '\t':
+		c = line[linep++];
+		goto loop;
+	case 0xd7:
+		return 0x2a;
+	case 0xf7:
+		return 0x2f;
+	case 0xb9:
+	case 0x2071:
+		yylval.numb = 1;
+		return SUP;
+	case 0xb2:
+	case 0x2072:
+		yylval.numb = 2;
+		return SUP;
+	case 0xb3:
+	case 0x2073:
+		yylval.numb = 3;
+		return SUP;
+	}
+	return c;
+
+alpha:
+	memset(sym, 0, sizeof(sym));
+	for(i=0;; i++) {
+		if(i < nelem(sym))
+			sym[i] = c;
+		c = line[linep++];
+		if(!ralpha(c))
+			break;
+	}
+	sym[nelem(sym)-1] = 0;
+	peekrune = c;
+	yylval.var = lookup(0);
+	return VAR;
+
+numb:
+	digval = c;
+	yylval.val = fmtcharstod(gdigit, 0);
+	return VAL;
+}
+
+void
+main(int argc, char *argv[])
+{
+	char *file;
+
+	ARGBEGIN {
+	default:
+		print("usage: units [-v] [file]\n");
+		exits("usage");
+	case 'v':
+		vflag = 1;
+		break;
+	} ARGEND
+
+	file = unsharp("#9/lib/units");
+	if(argc > 0)
+		file = argv[0];
+	fi = Bopen(file, OREAD);
+	if(fi == 0) {
+		print("cant open: %s\n", file);
+		exits("open");
+	}
+	fmtinstall('U', Ufmt);
+	one.val = 1;
+
+	/*
+	 * read the 'units' file to
+	 * develope a database
+	 */
+	lineno = 0;
+	for(;;) {
+		lineno++;
+		if(readline())
+			break;
+		if(line[0] == 0 || line[0] == '/')
+			continue;
+		peekrune = ':';
+		yyparse();
+	}
+
+	/*
+	 * read the console to
+	 * print ratio of pairs
+	 */
+	Bterm(fi);
+	fi = &linebuf;
+	Binit(fi, 0, OREAD);
+	lineno = 0;
+	for(;;) {
+		if(lineno & 1)
+			print("you want: ");
+		else
+			print("you have: ");
+		if(readline())
+			break;
+		peekrune = '?';
+		nerrors = 0;
+		yyparse();
+		if(nerrors)
+			continue;
+		if(lineno & 1) {
+			if(specialcase(&retnode, &retnode2, &retnode1))
+				print("\tis %U\n", &retnode);
+			else {
+				div(&retnode, &retnode2, &retnode1);
+				print("\t* %U\n", &retnode);
+				div(&retnode, &retnode1, &retnode2);
+				print("\t/ %U\n", &retnode);
+			}
+		} else
+			retnode2 = retnode1;
+		lineno++;
+	}
+	print("\n");
+	exits(0);
+}
+
+/*
+ * all characters that have some
+ * meaning. rest are usable as names
+ */
+int
+ralpha(int c)
+{
+	switch(c) {
+	case 0:
+	case '+':
+	case '-':
+	case '*':
+	case '/':
+	case '[':
+	case ']':
+	case '(':
+	case ')':
+	case '^':
+	case ':':
+	case '?':
+	case ' ':
+	case '\t':
+	case '.':
+	case '|':
+	case '#':
+	case 0xb9:
+	case 0x2071:
+	case 0xb2:
+	case 0x2072:
+	case 0xb3:
+	case 0x2073:
+	case 0xd7:
+	case 0xf7:
+		return 0;
+	}
+	return 1;
+}
+
+int
+gdigit(void *v)
+{
+	int c;
+
+	USED(v);
+	c = digval;
+	if(c) {
+		digval = 0;
+		return c;
+	}
+	c = line[linep++];
+	peekrune = c;
+	return c;
+}
+
+void
+yyerror(char *fmt, ...)
+{
+	va_list arg;
+
+	/*
+	 * hack to intercept message from yaccpar
+	 */
+	if(strcmp(fmt, "syntax error") == 0) {
+		yyerror("syntax error, last name: %S", sym);
+		return;
+	}
+	va_start(arg, fmt);
+	vseprint(buf, buf+sizeof(buf), fmt, arg);
+	va_end(arg);
+	print("%ld: %S\n\t%s\n", lineno, line, buf);
+	nerrors++;
+	if(nerrors > 5) {
+		print("too many errors\n");
+		exits("errors");
+	}
+}
+
+void
+add(Node *c, Node *a, Node *b)
+{
+	int i, d;
+
+	for(i=0; i<Ndim; i++) {
+		d = a->dim[i];
+		c->dim[i] = d;
+		if(d != b->dim[i])
+			yyerror("add must be like units");
+	}
+	c->val = fadd(a->val, b->val);
+}
+
+void
+sub(Node *c, Node *a, Node *b)
+{
+	int i, d;
+
+	for(i=0; i<Ndim; i++) {
+		d = a->dim[i];
+		c->dim[i] = d;
+		if(d != b->dim[i])
+			yyerror("sub must be like units");
+	}
+	c->val = fadd(a->val, -b->val);
+}
+
+void
+mul(Node *c, Node *a, Node *b)
+{
+	int i;
+
+	for(i=0; i<Ndim; i++)
+		c->dim[i] = a->dim[i] + b->dim[i];
+	c->val = fmul(a->val, b->val);
+}
+
+void
+div(Node *c, Node *a, Node *b)
+{
+	int i;
+
+	for(i=0; i<Ndim; i++)
+		c->dim[i] = a->dim[i] - b->dim[i];
+	c->val = fdiv(a->val, b->val);
+}
+
+void
+xpn(Node *c, Node *a, int b)
+{
+	int i;
+
+	*c = one;
+	if(b < 0) {
+		b = -b;
+		for(i=0; i<b; i++)
+			div(c, c, a);
+	} else
+	for(i=0; i<b; i++)
+		mul(c, c, a);
+}
+
+int
+specialcase(Node *c, Node *a, Node *b)
+{
+	int i, d, d1, d2;
+
+	d1 = 0;
+	d2 = 0;
+	for(i=1; i<Ndim; i++) {
+		d = a->dim[i];
+		if(d) {
+			if(d != 1 || d1)
+				return 0;
+			d1 = i;
+		}
+		d = b->dim[i];
+		if(d) {
+			if(d != 1 || d2)
+				return 0;
+			d2 = i;
+		}
+	}
+	if(d1 == 0 || d2 == 0)
+		return 0;
+
+	if(memcmp(fund[d1]->name, L"°C", 3*sizeof(Rune)) == 0 &&
+	   memcmp(fund[d2]->name, L"°F", 3*sizeof(Rune)) == 0 &&
+	   b->val == 1) {
+		memcpy(c->dim, b->dim, sizeof(c->dim));
+		c->val = a->val * 9. / 5. + 32.;
+		return 1;
+	}
+
+	if(memcmp(fund[d1]->name, L"°F", 3*sizeof(Rune)) == 0 &&
+	   memcmp(fund[d2]->name, L"°C", 3*sizeof(Rune)) == 0 &&
+	   b->val == 1) {
+		memcpy(c->dim, b->dim, sizeof(c->dim));
+		c->val = (a->val - 32.) * 5. / 9.;
+		return 1;
+	}
+	return 0;
+}
+
+void
+printdim(char *str, int d, int n)
+{
+	Var *v;
+
+	if(n) {
+		v = fund[d];
+		if(v)
+			sprint(strchr(str, 0), " %S", v->name);
+		else
+			sprint(strchr(str, 0), " [%d]", d);
+		switch(n) {
+		case 1:
+			break;
+		case 2:
+			strcat(str, "²");
+			break;
+		case 3:
+			strcat(str, "³");
+			break;
+		default:
+			sprint(strchr(str, 0), "^%d", n);
+		}
+	}
+}
+
+int
+Ufmt(Fmt *fp)
+{
+	char str[200];
+	Node *n;
+	int f, i, d;
+
+	n = va_arg(fp->args, Node*);
+	sprint(str, "%g", n->val);
+
+	f = 0;
+	for(i=1; i<Ndim; i++) {
+		d = n->dim[i];
+		if(d > 0)
+			printdim(str, i, d);
+		else
+		if(d < 0)
+			f = 1;
+	}
+
+	if(f) {
+		strcat(str, " /");
+		for(i=1; i<Ndim; i++) {
+			d = n->dim[i];
+			if(d < 0)
+				printdim(str, i, -d);
+		}
+	}
+
+	return fmtstrcpy(fp, str);
+}
+
+int
+readline(void)
+{
+	int i, c;
+
+	linep = 0;
+	for(i=0;; i++) {
+		c = Bgetrune(fi);
+		if(c < 0)
+			return 1;
+		if(c == '\n')
+			break;
+		if(i < nelem(line))
+			line[i] = c;
+	}
+	if(i >= nelem(line))
+		i = nelem(line)-1;
+	line[i] = 0;
+	return 0;
+}
+
+Var*
+lookup(int f)
+{
+	int i;
+	Var *v, *w;
+	double p;
+	ulong h;
+
+	h = 0;
+	for(i=0; sym[i]; i++)
+		h = h*13 + sym[i];
+	h %= nelem(vars);
+
+	for(v=vars[h]; v; v=v->link)
+		if(memcmp(sym, v->name, sizeof(sym)) == 0)
+			return v;
+	if(f)
+		return 0;
+	v = malloc(sizeof(*v));
+	if(v == nil) {
+		fprint(2, "out of memory\n");
+		exits("mem");
+	}
+	memset(v, 0, sizeof(*v));
+	memcpy(v->name, sym, sizeof(sym));
+	v->link = vars[h];
+	vars[h] = v;
+
+	p = 1;
+	for(;;) {
+		p = fmul(p, pname());
+		if(p == 0)
+			break;
+		w = lookup(1);
+		if(w) {
+			v->node = w->node;
+			v->node.val = fmul(v->node.val, p);
+			break;
+		}
+	}
+	return v;
+}
+
+Prefix	prefix[] =
+{
+	1e-24,	"yocto", 0,
+	1e-21,	"zepto", 0,
+	1e-18,	"atto", 0,
+	1e-15,	"femto", 0,
+	1e-12,	"pico", 0,
+	1e-9,	"nano", 0,
+	1e-6,	"micro", 0,
+	1e-6,	"μ", 0,
+	1e-3,	"milli", 0,
+	1e-2,	"centi", 0,
+	1e-1,	"deci", 0,
+	1e1,	"deka", 0,
+	1e2,	"hecta", 0,
+	1e2,	"hecto", 0,
+	1e3,	"kilo", 0,
+	1e6,	"mega", 0,
+	1e6,	"meg", 0,
+	1e9,	"giga", 0,
+	1e12,	"tera", 0,
+	1e15,	"peta", 0,
+	1e18,	"exa", 0,
+	1e21,	"zetta", 0,
+	1e24,	"yotta", 0,
+	0,	0, 0,
+};
+
+double
+pname(void)
+{
+	Rune *p;
+	int i, j, c;
+
+	/*
+	 * rip off normal prefixs
+	 */
+	if(prefix[0].pname == nil){
+		for(i=0; prefix[i].name; i++)
+			prefix[i].pname = runesmprint("%s", prefix[i].name);
+	}
+
+	for(i=0; p=prefix[i].pname; i++) {
+		for(j=0; c=p[j]; j++)
+			if(c != sym[j])
+				goto no;
+		memmove(sym, sym+j, (Nsym-j)*sizeof(*sym));
+		memset(sym+(Nsym-j), 0, j*sizeof(*sym));
+		return prefix[i].val;
+	no:;
+	}
+
+	/*
+	 * rip off 's' suffixes
+	 */
+	for(j=0; sym[j]; j++)
+		;
+	j--;
+	/* j>1 is special hack to disallow ms finding m */
+	if(j > 1 && sym[j] == 's') {
+		sym[j] = 0;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * careful floating point
+ */
+double
+fmul(double a, double b)
+{
+	double l;
+
+	if(a <= 0) {
+		if(a == 0)
+			return 0;
+		l = log(-a);
+	} else
+		l = log(a);
+
+	if(b <= 0) {
+		if(b == 0)
+			return 0;
+		l += log(-b);
+	} else
+		l += log(b);
+
+	if(l > Maxe) {
+		yyerror("overflow in multiply");
+		return 1;
+	}
+	if(l < -Maxe) {
+		yyerror("underflow in multiply");
+		return 0;
+	}
+	return a*b;
+}
+
+double
+fdiv(double a, double b)
+{
+	double l;
+
+	if(a <= 0) {
+		if(a == 0)
+			return 0;
+		l = log(-a);
+	} else
+		l = log(a);
+
+	if(b <= 0) {
+		if(b == 0) {
+			yyerror("division by zero");
+			return 1;
+		}
+		l -= log(-b);
+	} else
+		l -= log(b);
+
+	if(l > Maxe) {
+		yyerror("overflow in divide");
+		return 1;
+	}
+	if(l < -Maxe) {
+		yyerror("underflow in divide");
+		return 0;
+	}
+	return a/b;
+}
+
+double
+fadd(double a, double b)
+{
+	return a + b;
+}