Commit Diff


commit - f08fdedcee12c06e3ce9ac9bec363915978e8289
commit + bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d
blob - /dev/null
blob + ee2cb4cafa2c13743496b5a673d3dd37425c265e (mode 644)
--- /dev/null
+++ src/cmd/ascii.c
@@ -0,0 +1,181 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define	MAXBASE	36
+
+void	usage(void);
+void	put(int);
+void	putn(int, int);
+void	puttext(char *);
+void	putnum(char *);
+int	btoi(char *);
+int	value(int, int);
+int	isnum(char *);
+
+char *str[256]={
+	"nul",	"soh",	"stx",	"etx",	"eot",	"enq",	"ack",	"bel",
+	"bs ",	"ht ",	"nl ",	"vt ",	"np ",	"cr ",	"so ",	"si ",
+	"dle",	"dc1",	"dc2",	"dc3",	"dc4",	"nak",	"syn",	"etb",
+	"can",	"em ",	"sub",	"esc",	"fs ",	"gs ",	"rs ",	"us ",
+	"sp ",	" ! ",	" \" ",	" # ",	" $ ",	" % ",	" & ",	" ' ",
+	" ( ",	" ) ",	" * ",	" + ",	" , ",	" - ",	" . ",	" / ",
+	" 0 ",	" 1 ",	" 2 ",	" 3 ",	" 4 ",	" 5 ",	" 6 ",	" 7 ",
+	" 8 ",	" 9 ",	" : ",	" ; ",	" < ",	" = ",	" > ",	" ? ",
+	" @ ",	" 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 ",	" [ ",	" \\ ",	" ] ",	" ^ ",	" _ ",
+	" ` ",	" 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 ",	" { ",	" | ",	" } ",	" ~ ",	"del",
+	"x80",	"x81",	"x82",	"x83",	"x84",	"x85",	"x86",	"x87",
+	"x88",	"x89",	"x8a",	"x8b",	"x8c",	"x8d",	"x8e",	"x8f",
+	"x90",	"x91",	"x92",	"x93",	"x94",	"x95",	"x96",	"x97",
+	"x98",	"x99",	"x9a",	"x9b",	"x9c",	"x9d",	"x9e",	"x9f",
+	"xa0",	" ¡ ",	" ¢ ",	" £ ",	" ¤ ",	" ¥ ",	" ¦ ",	" § ",
+	" ¨ ",	" © ",	" ª ",	" « ",	" ¬ ",	" ­ ",	" ® ",	" ¯ ",
+	" ° ",	" ± ",	" ² ",	" ³ ",	" ´ ",	" µ ",	" ¶ ",	" · ",
+	" ¸ ",	" ¹ ",	" º ",	" » ",	" ¼ ",	" ½ ",	" ¾ ",	" ¿ ",
+	" À ",	" Á ",	" Â ",	" Ã ",	" Ä ",	" Å ",	" Æ ",	" Ç ",
+	" È ",	" É ",	" Ê ",	" Ë ",	" Ì ",	" Í ",	" Î ",	" Ï ",
+	" Ð ",	" Ñ ",	" Ò ",	" Ó ",	" Ô ",	" Õ ",	" Ö ",	" × ",
+	" Ø ",	" Ù ",	" Ú ",	" Û ",	" Ü ",	" Ý ",	" Þ ",	" ß ",
+	" à ",	" á ",	" â ",	" ã ",	" ä ",	" å ",	" æ ",	" ç ",
+	" è ",	" é ",	" ê ",	" ë ",	" ì ",	" í ",	" î ",	" ï ",
+	" ð ",	" ñ ",	" ò ",	" ó ",	" ô ",	" õ ",	" ö ",	" ÷ ",
+	" ø ",	" ù ",	" ú ",	" û ",	" ü ",	" ý ",	" þ ",	" ÿ "
+};
+
+char Ncol[]={
+    0,0,7,5,4,4,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+};
+
+int 	nchars=128;
+int 	base=16;
+int 	ncol;
+int 	text=1;
+int	strip=0;
+Biobuf	bin;
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	Binit(&bin, 1, OWRITE);
+	ARGBEGIN{
+	case '8':
+		nchars=256; break;
+	case 'x':
+		base=16; break;
+	case 'o':
+		base=8; break;
+	case 'd':
+		base=10; break;
+	case 'b':
+		base=strtoul(EARGF(usage()), 0, 0);
+		if(base<2||base>MAXBASE)
+			usage();
+		break;
+	case 'n':
+		text=0; break;
+	case 't':
+		strip=1;
+		/* fall through */
+	case 'c':
+		text=2; break;
+	default:
+		usage();
+	}ARGEND
+
+	ncol=Ncol[base];
+	if(argc==0){
+		for(i=0;i<nchars;i++){
+			put(i);
+			if((i&7)==7)
+				Bprint(&bin, "|\n");
+		}
+	}else{
+		if(text==1)
+			text=isnum(argv[0]);
+		while(argc--)
+			if(text)
+				puttext(*argv++);
+			else
+				putnum(*argv++);
+	}
+	Bputc(&bin, '\n');
+	exits(0);
+}
+void
+usage(void)
+{
+	fprint(2, "usage: %s [-8] [-xod | -b8] [-ncst] [--] [text]\n", argv0);
+	exits("usage");
+}
+void
+put(int i)
+{
+	Bputc(&bin, '|');
+	putn(i, ncol);
+	Bprint(&bin, " %s", str[i]);
+}
+char dig[]="0123456789abcdefghijklmnopqrstuvwxyz";
+void
+putn(int n, int ndig)
+{
+	if(ndig==0)
+		return;
+	putn(n/base, ndig-1);
+	Bputc(&bin, dig[n%base]);
+}
+void
+puttext(char *s)
+{
+	int n;
+	n=btoi(s)&0377;
+	if(strip)
+		Bputc(&bin, n);
+	else
+		Bprint(&bin, "%s\n", str[n]);
+}
+void
+putnum(char *s)
+{
+	while(*s){
+		putn(*s++&0377, ncol);
+		Bputc(&bin, '\n');
+	}
+}
+int
+btoi(char *s)
+{
+	int n;
+	n=0;
+	while(*s)
+		n=n*base+value(*s++, 0);
+	return(n);
+}
+int
+value(int c, int f)
+{
+	char *s;
+	for(s=dig; s<dig+base; s++)
+		if(*s==c)
+			return(s-dig);
+	if(f)
+		return(-1);
+	fprint(2, "%s: bad input char %c\n", argv0, c);
+	exits("bad");
+	return 0;	/* to keep ken happy */
+}
+int
+isnum(char *s)
+{
+	while(*s)
+		if(value(*s++, 1)==-1)
+			return(0);
+	return(1);
+}
blob - /dev/null
blob + 7b274b2281c91ee8b1b7eb4b789c0819eb5ebe7d (mode 644)
--- /dev/null
+++ src/cmd/basename.c
@@ -0,0 +1,41 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+	char *pr;
+	int n, dflag;
+
+	dflag = 0;
+	if(argc>1 && strcmp(argv[1], "-d") == 0){
+		--argc;
+		++argv;
+		dflag = 1;
+	}
+	if(argc < 2 || argc > 3){
+		fprint(2, "usage: basename [-d] string [suffix]\n");
+		exits("usage");
+	}
+	pr = utfrrune(argv[1], '/');
+	if(dflag){
+		if(pr){
+			*pr = 0;
+			print("%s\n", argv[1]);
+			exits(0);
+		}
+		print(".\n");
+		exits(0);
+	}
+	if(pr)
+		pr++;
+	else
+		pr = argv[1];
+	if(argc==3){
+		n = strlen(pr)-strlen(argv[2]);
+		if(n >= 0 && !strcmp(pr+n, argv[2]))
+			pr[n] = 0;
+	}
+	print("%s\n", pr);
+	exits(0);
+}
blob - /dev/null
blob + 113069b692739a12c58550a88d5b548806a098d8 (mode 644)
--- /dev/null
+++ src/cmd/cal.c
@@ -0,0 +1,313 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+char	dayw[] =
+{
+	" S  M Tu  W Th  F  S"
+};
+char	*smon[] =
+{
+	"January", "February", "March", "April",
+	"May", "June", "July", "August",
+	"September", "October", "November", "December",
+};
+char	mon[] =
+{
+	0,
+	31, 29, 31, 30,
+	31, 30, 31, 31,
+	30, 31, 30, 31,
+};
+char	string[432];
+Biobuf	bout;
+
+void	main(int argc, char *argv[]);
+int	number(char *str);
+void	pstr(char *str, int n);
+void	cal(int m, int y, char *p, int w);
+int	jan1(int yr);
+int	curmo(void);
+int	curyr(void);
+
+void
+main(int argc, char *argv[])
+{
+	int y, i, j, m;
+
+	if(argc > 3) {
+		fprint(2, "usage: cal [month] [year]\n");
+		exits("usage");
+	}
+	Binit(&bout, 1, OWRITE);
+
+/*
+ * no arg, print current month
+ */
+	if(argc == 1) {
+		m = curmo();
+		y = curyr();
+		goto xshort;
+	}
+
+/*
+ * one arg
+ *	if looks like a month, print month
+ *	else print year
+ */
+	if(argc == 2) {
+		y = number(argv[1]);
+		if(y < 0)
+			y = -y;
+		if(y >= 1 && y <= 12) {
+			m = y;
+			y = curyr();
+			goto xshort;
+		}
+		goto xlong;
+	}
+
+/*
+ * two arg, month and year
+ */
+	m = number(argv[1]);
+	if(m < 0)
+		m = -m;
+	y = number(argv[2]);
+	goto xshort;
+
+/*
+ *	print out just month
+ */
+xshort:
+	if(m < 1 || m > 12)
+		goto badarg;
+	if(y < 1 || y > 9999)
+		goto badarg;
+	Bprint(&bout, "   %s %ud\n", smon[m-1], y);
+	Bprint(&bout, "%s\n", dayw);
+	cal(m, y, string, 24);
+	for(i=0; i<6*24; i+=24)
+		pstr(string+i, 24);
+	exits(0);
+
+/*
+ *	print out complete year
+ */
+xlong:
+	y = number(argv[1]);
+	if(y<1 || y>9999)
+		goto badarg;
+	Bprint(&bout, "\n\n\n");
+	Bprint(&bout, "                                %ud\n", y);
+	Bprint(&bout, "\n");
+	for(i=0; i<12; i+=3) {
+		for(j=0; j<6*72; j++)
+			string[j] = '\0';
+		Bprint(&bout, "         %.3s", smon[i]);
+		Bprint(&bout, "                    %.3s", smon[i+1]);
+		Bprint(&bout, "                    %.3s\n", smon[i+2]);
+		Bprint(&bout, "%s   %s   %s\n", dayw, dayw, dayw);
+		cal(i+1, y, string, 72);
+		cal(i+2, y, string+23, 72);
+		cal(i+3, y, string+46, 72);
+		for(j=0; j<6*72; j+=72)
+			pstr(string+j, 72);
+	}
+	Bprint(&bout, "\n\n\n");
+	exits(0);
+
+badarg:
+	Bprint(&bout, "cal: bad argument\n");
+}
+
+struct
+{
+	char*	word;
+	int	val;
+} dict[] =
+{
+	"jan",		1,
+	"january",	1,
+	"feb",		2,
+	"february",	2,
+	"mar",		3,
+	"march",	3,
+	"apr",		4,
+	"april",	4,
+	"may",		5,
+	"jun",		6,
+	"june",		6,
+	"jul",		7,
+	"july",		7,
+	"aug",		8,
+	"august",	8,
+	"sep",		9,
+	"sept",		9,
+	"september",	9,
+	"oct",		10,
+	"october",	10,
+	"nov",		11,
+	"november",	11,
+	"dec",		12,
+	"december",	12,
+	0
+};
+
+/*
+ * convert to a number.
+ * if its a dictionary word,
+ * return negative  number
+ */
+int
+number(char *str)
+{
+	int n, c;
+	char *s;
+
+	for(n=0; s=dict[n].word; n++)
+		if(strcmp(s, str) == 0)
+			return -dict[n].val;
+	n = 0;
+	s = str;
+	while(c = *s++) {
+		if(c<'0' || c>'9')
+			return 0;
+		n = n*10 + c-'0';
+	}
+	return n;
+}
+
+void
+pstr(char *str, int n)
+{
+	int i;
+	char *s;
+
+	s = str;
+	i = n;
+	while(i--)
+		if(*s++ == '\0')
+			s[-1] = ' ';
+	i = n+1;
+	while(i--)
+		if(*--s != ' ')
+			break;
+	s[1] = '\0';
+	Bprint(&bout, "%s\n", str);
+}
+
+void
+cal(int m, int y, char *p, int w)
+{
+	int d, i;
+	char *s;
+
+	s = p;
+	d = jan1(y);
+	mon[2] = 29;
+	mon[9] = 30;
+
+	switch((jan1(y+1)+7-d)%7) {
+
+	/*
+	 *	non-leap year
+	 */
+	case 1:
+		mon[2] = 28;
+		break;
+
+	/*
+	 *	1752
+	 */
+	default:
+		mon[9] = 19;
+		break;
+
+	/*
+	 *	leap year
+	 */
+	case 2:
+		;
+	}
+	for(i=1; i<m; i++)
+		d += mon[i];
+	d %= 7;
+	s += 3*d;
+	for(i=1; i<=mon[m]; i++) {
+		if(i==3 && mon[m]==19) {
+			i += 11;
+			mon[m] += 11;
+		}
+		if(i > 9)
+			*s = i/10+'0';
+		s++;
+		*s++ = i%10+'0';
+		s++;
+		if(++d == 7) {
+			d = 0;
+			s = p+w;
+			p = s;
+		}
+	}
+}
+
+/*
+ *	return day of the week
+ *	of jan 1 of given year
+ */
+int
+jan1(int yr)
+{
+	int y, d;
+
+/*
+ *	normal gregorian calendar
+ *	one extra day per four years
+ */
+
+	y = yr;
+	d = 4+y+(y+3)/4;
+
+/*
+ *	julian calendar
+ *	regular gregorian
+ *	less three days per 400
+ */
+
+	if(y > 1800) {
+		d -= (y-1701)/100;
+		d += (y-1601)/400;
+	}
+
+/*
+ *	great calendar changeover instant
+ */
+
+	if(y > 1752)
+		d += 3;
+
+	return d%7;
+}
+
+/*
+ * system dependent
+ * get current month and year
+ */
+int
+curmo(void)
+{
+	Tm *tm;
+
+	tm = localtime(time(0));
+	return tm->mon+1;
+}
+
+int
+curyr(void)
+{
+	Tm *tm;
+
+	tm = localtime(time(0));
+	return tm->year+1900;
+}
blob - /dev/null
blob + 6202c5a35fa2682a9c7103f90ebf306975c52250 (mode 644)
--- /dev/null
+++ src/cmd/calendar.c
@@ -0,0 +1,195 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+#include <ctype.h>
+
+typedef struct Date	Date;
+struct Date {
+	Reprog *p;	/* an RE to match this date */
+	Date *next;	/* pointer to next in list */
+};
+
+enum{
+	Secondsperday = 24*60*60
+};
+
+Biobuf in;
+int debug, matchyear;
+
+Date *dates(Date**, Tm*);
+void upper2lower(char*, char*, int);
+void *alloc(unsigned int);
+
+void
+main(int argc, char *argv[])
+{
+	int i, fd, ahead;
+	long now;
+	char *line;
+	Tm *tm;
+	Date *first, *last, *d;
+	char buf[1024];
+
+	ahead = 0;
+	ARGBEGIN{
+	case 'y':
+		matchyear = 1;
+		break;
+	case 'd':
+		debug = 1;
+		break;
+	case 'p':
+		ahead = atoi(ARGF());
+		break;
+	default:
+		fprint(2, "usage: calendar [-y] [-d] [files ...]\n");
+		exits("usage");
+	}ARGEND;
+
+	/* make a list of dates */
+	now = time(0);
+	tm = localtime(now);
+	last = nil;
+	first = dates(&last, tm);
+	now += Secondsperday;
+	tm = localtime(now);
+	dates(&last, tm);
+	if(tm->wday == 6){
+		now += Secondsperday;
+		tm = localtime(now);
+		dates(&last, tm);
+	}
+	if(tm->wday == 0){
+		now += Secondsperday;
+		tm = localtime(now);
+		dates(&last, tm);
+	}
+	if(ahead){
+		now = time(0);
+		now += ahead * Secondsperday;
+		tm = localtime(now);
+		dates(&last, tm);
+	}
+
+	for(i=0; i<argc || (i==0 && argc==0); i++){
+		if(i==0 && argc==0)
+			snprint(buf, sizeof(buf),
+				"/usr/%s/lib/calendar", getuser());
+		else
+			strcpy(buf, argv[i]);
+		fd = open(buf, OREAD);
+		if(fd<0 || Binit(&in, fd, OREAD)<0){
+			fprint(2, "calendar: can't open %s: %r\n", buf);
+			exits("open");
+		}
+
+		/* go through the file */
+		while(line = Brdline(&in, '\n')){
+			line[Blinelen(&in) - 1] = 0;
+			upper2lower(buf, line, sizeof buf);
+			for(d=first; d; d=d->next)
+				if(regexec(d->p, buf, 0, 0)){
+					print("%s\n", line);
+					break;
+				}
+		}
+		close(fd);
+	}
+	exits("");
+}
+
+char *months[] = 
+{
+	"january",
+	"february",
+	"march",
+	"april",
+	"may",
+	"june",
+	"july",
+	"august",
+	"september",
+	"october",
+	"november",
+	"december"
+};
+
+/*
+ * Generate two Date structures.  First has month followed by day;
+ * second has day followed by month.  Link them into list after
+ * last, and return the first.
+ */
+Date*
+dates(Date **last, Tm *tm)
+{
+	Date *first;
+	Date *nd;
+	char mo[128], buf[128];
+
+	if(utflen(months[tm->mon]) > 3)
+		snprint(mo, sizeof mo, "%3.3s(%s)?",
+			months[tm->mon], months[tm->mon]+3);
+	else
+		snprint(mo, sizeof mo, "%3.3s", months[tm->mon]);
+	if (matchyear)
+		snprint(buf, sizeof buf,
+			"(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))",
+			mo, tm->mon+1, tm->mday, tm->year+1900, tm->year%100);
+	else
+		snprint(buf, sizeof buf,
+			"(^| |\t)((%s( |\t)+)|(%d/))%d( |\t|$)",
+			mo, tm->mon+1, tm->mday);
+	if(debug)
+		print("%s\n", buf);
+
+	first = alloc(sizeof(Date));
+	if(*last)
+		(*last)->next = first;
+	first->p = regcomp(buf);	
+
+	if (matchyear)
+		snprint(buf, sizeof buf,
+			"(^| |\t)%d( |\t)+(%s)( |\t|$)(((%d|%d)( |\t|$))|[^0-9]|([0-9]+[^0-9 \t]))",
+			tm->mday, mo, tm->year+1900, tm->year%100);
+	else
+		snprint(buf, sizeof buf,
+			"(^| |\t)%d( |\t)+(%s)( |\t|$)",
+			tm->mday, mo);
+	if(debug)
+		print("%s\n", buf);
+	nd = alloc(sizeof(Date));
+	nd->p = regcomp(buf);	
+	nd->next = 0;
+	first->next = nd;
+	*last = nd;
+
+	return first;
+}
+
+/*
+ * Copy 'from' to 'to', converting to lower case
+ */
+void
+upper2lower(char *to, char *from, int len)
+{
+	while(--len>0 && *from!='\0')
+		*to++ = tolower(*from++);
+	*to = 0;
+}
+
+/*
+ * Call malloc and check for errors
+ */
+void*
+alloc(unsigned int n)
+{
+	void *p;
+
+	p = malloc(n);
+	if(p == 0){
+		fprint(2, "calendar: malloc failed: %r\n");
+		exits("malloc");
+	}
+	return p;
+}
blob - /dev/null
blob + ac5888979725984641e4d2e247d40ec8f626d313 (mode 644)
--- /dev/null
+++ src/cmd/cat.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+
+void
+cat(int f, char *s)
+{
+	char buf[8192];
+	long n;
+
+	while((n=read(f, buf, (long)sizeof buf))>0)
+		if(write(1, buf, n)!=n)
+			sysfatal("write error copying %s: %r", s);
+	if(n < 0)
+		sysfatal("error reading %s: %r", s);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int f, i;
+
+	argv0 = "cat";
+	if(argc == 1)
+		cat(0, "<stdin>");
+	else for(i=1; i<argc; i++){
+		f = open(argv[i], OREAD);
+		if(f < 0)
+			sysfatal("can't open %s: %r", argv[i]);
+		else{
+			cat(f, argv[i]);
+			close(f);
+		}
+	}
+	exits(0);
+}
+
blob - /dev/null
blob + 8fa1846661a95051088962ea3d4d0c8a0281bdea (mode 644)
--- /dev/null
+++ src/cmd/cleanname.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char **argv)
+{
+	char *dir;
+	char *name;
+	int i;
+
+	dir = nil;
+	ARGBEGIN{
+	case 'd':
+		if((dir=ARGF()) == nil)
+			goto Usage;
+		break;
+	default:
+		goto Usage;
+	}ARGEND;
+
+	if(argc < 1) {
+	Usage:
+		fprint(2, "usage: cleanname [-d pwd] name...\n");
+		exits("usage");
+	}
+
+	for(i=0; i<argc; i++) {
+		if(dir == nil || argv[i][0] == '/') {
+			cleanname(argv[i]);
+			print("%s\n", argv[i]);
+		} else {
+			name = malloc(strlen(argv[i])+1+strlen(dir)+1);
+			if(name == nil) {
+				fprint(2, "cleanname: out of memory\n");
+				exits("out of memory");
+			}
+			sprint(name, "%s/%s", dir, argv[i]);
+			cleanname(name);
+			print("%s\n", name);
+			free(name);
+		}
+	}
+	exits(0);
+}
blob - /dev/null
blob + 4f69e181597666c8855656231c2d7baefa32d7a8 (mode 644)
--- /dev/null
+++ src/cmd/cmp.c
@@ -0,0 +1,112 @@
+#include <u.h>
+#include <libc.h>
+
+#define		BUF		65536
+
+int sflag = 0;
+int lflag = 0;
+int Lflag = 0;
+
+static void usage(void);
+
+void
+main(int argc, char *argv[])
+{
+	int n, i;
+	uchar *p, *q;
+	uchar buf1[BUF], buf2[BUF];
+	int f1, f2;
+	vlong nc = 1, o, l = 1;
+	char *name1, *name2;
+	uchar *b1s, *b1e, *b2s, *b2e;
+
+	ARGBEGIN{
+	case 's':	sflag = 1; break;
+	case 'l':	lflag = 1; break;
+	case 'L':	Lflag = 1; break;
+	default:	usage();
+	}ARGEND
+	if(argc < 2)
+		usage();
+	if((f1 = open(name1 = *argv++, OREAD)) == -1){
+		if(!sflag) perror(name1);
+		exits("open");
+	}
+	if((f2 = open(name2 = *argv++, OREAD)) == -1){
+		if(!sflag) perror(name2);
+		exits("open");
+	}
+	if(*argv){
+		o = strtoll(*argv++, 0, 0);
+		if(seek(f1, o, 0) < 0){
+			if(!sflag) perror("cmp: seek by offset1");
+			exits("seek 1");
+		}
+	}
+	if(*argv){
+		o = strtoll(*argv++, 0, 0);
+		if(seek(f2, o, 0) < 0){
+			if(!sflag) perror("cmp: seek by offset2");
+			exits("seek 2");
+		}
+	}
+	if(*argv)
+		usage();
+	b1s = b1e = buf1;
+	b2s = b2e = buf2;
+	for(;;){
+		if(b1s >= b1e){
+			if(b1s >= &buf1[BUF])
+				b1s = buf1;
+			n = read(f1, b1s,  &buf1[BUF] - b1s);
+			b1e = b1s + n;
+		}
+		if(b2s >= b2e){
+			if(b2s >= &buf2[BUF])
+				b2s = buf2;
+			n = read(f2, b2s,  &buf2[BUF] - b2s);
+			b2e = b2s + n;
+		}
+		n = b2e - b2s;
+		if(n > b1e - b1s)
+			n = b1e - b1s;
+		if(n <= 0)
+			break;
+		if(memcmp((void *)b1s, (void *)b2s, n) != 0){
+			if(sflag)
+				exits("differ");
+			for(p = b1s, q = b2s, i = 0; i < n; p++, q++, i++) {
+				if(*p == '\n')
+					l++;
+				if(*p != *q){
+					if(!lflag){
+						print("%s %s differ: char %lld",
+						    name1, name2, nc+i);
+						print(Lflag?" line %lld\n":"\n", l);
+						exits("differ");
+					}
+					print("%6lld 0x%.2x 0x%.2x\n", nc+i, *p, *q);
+				}
+			}
+		}		
+		if(Lflag)
+			for(p = b1s; p < b1e;)
+				if(*p++ == '\n')
+					l++;
+		nc += n;
+		b1s += n;
+		b2s += n;
+	}
+	if(b1e - b1s == b2e - b2s)
+		exits((char *)0);
+	if(!sflag)
+		print("EOF on %s\n", (b1e - b1s > b2e - b2s)? name2 : name1);
+	exits("EOF");
+}
+
+static void
+usage(void)
+{
+	print("Usage: cmp [-lsL] file1 file2 [offset1 [offset2] ]\n");
+	exits("usage");
+}
blob - /dev/null
blob + 8109c59cfe34de7578edcf5fed5b6b56d7aab669 (mode 644)
--- /dev/null
+++ src/cmd/comm.c
@@ -0,0 +1,178 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define LB 2048
+int	one;
+int	two;
+int	three;
+
+char	*ldr[3];
+
+Biobuf *ib1;
+Biobuf *ib2;
+Biobuf *openfil(char*);
+int	rd(Biobuf*, char*);
+void	wr(char*, int);
+void	copy(Biobuf*, char*, int);
+int	compare(char*, char*);
+
+void
+main(int argc, char *argv[])
+{
+	int l;
+	char	lb1[LB],lb2[LB];
+
+	ldr[0] = "";
+	ldr[1] = "\t";
+	ldr[2] = "\t\t";
+	l = 2;
+	ARGBEGIN{
+	case '1':
+		if(!one) {
+			one = 1;
+			ldr[1][0] =
+			ldr[2][l--] = '\0';
+		}
+		break;
+
+	case '2':
+		if(!two) {
+			two = 1;
+			ldr[2][l--] = '\0';
+		}
+		break;
+
+	case '3':
+		three = 1;
+		break;
+
+	default:
+		goto Usage;
+
+	}ARGEND
+
+	if(argc < 2) {
+    Usage:
+		fprint(2, "usage: comm [-123] file1 file2\n");
+		exits("usage");
+	}
+
+	ib1 = openfil(argv[0]);
+	ib2 = openfil(argv[1]);
+
+
+	if(rd(ib1,lb1) < 0){
+		if(rd(ib2,lb2) < 0)
+			exits(0);
+		copy(ib2,lb2,2);
+	}
+	if(rd(ib2,lb2) < 0)
+		copy(ib1,lb1,1);
+
+	for(;;){
+		switch(compare(lb1,lb2)) {
+		case 0:
+			wr(lb1,3);
+			if(rd(ib1,lb1) < 0) {
+				if(rd(ib2,lb2) < 0)
+					exits(0);
+				copy(ib2,lb2,2);
+			}
+			if(rd(ib2,lb2) < 0)
+				copy(ib1,lb1,1);
+			continue;
+
+		case 1:
+			wr(lb1,1);
+			if(rd(ib1,lb1) < 0)
+				copy(ib2,lb2,2);
+			continue;
+
+		case 2:
+			wr(lb2,2);
+			if(rd(ib2,lb2) < 0)
+				copy(ib1,lb1,1);
+			continue;
+		}
+	}
+	exits(0);
+}
+
+int
+rd(Biobuf *file, char *buf)
+{
+	int i, c;
+
+	i = 0;
+	while((c = Bgetc(file)) != Beof) {
+		*buf = c;
+		if(c == '\n' || i > LB-2) {
+			*buf = '\0';
+			return 0;
+		}
+		i++;
+		buf++;
+	}
+	return -1;
+}
+
+void
+wr(char *str, int n)
+{
+
+	switch(n){
+		case 1:
+			if(one)
+				return;
+			break;
+
+		case 2:
+			if(two)
+				return;
+			break;
+
+		case 3:
+			if(three)
+				return;
+	}
+	print("%s%s\n", ldr[n-1],str);
+}
+
+void
+copy(Biobuf *ibuf, char *lbuf, int n)
+{
+	do
+		wr(lbuf,n);
+	while(rd(ibuf,lbuf) >= 0);
+	exits(0);
+}
+
+int
+compare(char *a, char *b)
+{
+	while(*a == *b){
+		if(*a == '\0')
+			return 0;
+		a++;
+		b++;
+	}
+	if(*a < *b)
+		return 1;
+	return 2;
+}
+
+Biobuf*
+openfil(char *s)
+{
+	Biobuf *b;
+
+	if(s[0]=='-' && s[1]==0)
+		s = "/fd/0";
+	b = Bopen(s, OREAD);
+	if(b)
+		return b;
+	fprint(2,"comm: cannot open %s: %r\n",s);
+	exits("open");
+	return 0;	/* shut up ken */
+}
blob - /dev/null
blob + fc2ec5af5017e36bd97c8539a42ade35fae1df98 (mode 644)
--- /dev/null
+++ src/cmd/date.c
@@ -0,0 +1,30 @@
+#include <u.h>
+#include <libc.h>
+
+int uflg, nflg;
+
+void
+main(int argc, char *argv[])
+{
+	ulong now;
+
+	ARGBEGIN{
+	case 'n':	nflg = 1; break;
+	case 'u':	uflg = 1; break;
+	default:	fprint(2, "usage: date [-un] [seconds]\n"); exits("usage");
+	}ARGEND
+
+	if(argc == 1)
+		now = strtoul(*argv, 0, 0);
+	else
+		now = time(0);
+
+	if(nflg)
+		print("%ld\n", now);
+	else if(uflg)
+		print("%s", asctime(gmtime(now)));
+	else
+		print("%s", ctime(now));
+	
+	exits(0);
+}
blob - /dev/null
blob + 9fe4560df68151e249a19c5111c92e79015dc7b1 (mode 644)
--- /dev/null
+++ src/cmd/dc.c
@@ -0,0 +1,2300 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+typedef	void*	pointer;
+
+#define div	dcdiv
+
+#define FATAL 0
+#define NFATAL 1
+#define BLK sizeof(Blk)
+#define PTRSZ sizeof(int*)
+#define HEADSZ 1024
+#define STKSZ 100
+#define RDSKSZ 100
+#define TBLSZ 256
+#define ARRAYST 221
+#define MAXIND 2048
+#define NL 1
+#define NG 2
+#define NE 3
+#define length(p)	((p)->wt-(p)->beg)
+#define rewind(p)	(p)->rd=(p)->beg
+#define create(p)	(p)->rd = (p)->wt = (p)->beg
+#define fsfile(p)	(p)->rd = (p)->wt
+#define truncate(p)	(p)->wt = (p)->rd
+#define sfeof(p)	(((p)->rd==(p)->wt)?1:0)
+#define sfbeg(p)	(((p)->rd==(p)->beg)?1:0)
+#define sungetc(p,c)	*(--(p)->rd)=c
+#define sgetc(p)	(((p)->rd==(p)->wt)?-1:*(p)->rd++)
+#define skipc(p)	{if((p)->rd<(p)->wt)(p)->rd++;}
+#define slookc(p)	(((p)->rd==(p)->wt)?-1:*(p)->rd)
+#define sbackc(p)	(((p)->rd==(p)->beg)?-1:*(--(p)->rd))
+#define backc(p)	{if((p)->rd>(p)->beg) --(p)->rd;}
+#define sputc(p,c)	{if((p)->wt==(p)->last)more(p);\
+				*(p)->wt++ = c; }
+#define salterc(p,c)	{if((p)->rd==(p)->last)more(p);\
+				*(p)->rd++ = c;\
+				if((p)->rd>(p)->wt)(p)->wt=(p)->rd;}
+#define sunputc(p)	(*((p)->rd = --(p)->wt))
+#define sclobber(p)	((p)->rd = --(p)->wt)
+#define zero(p)		for(pp=(p)->beg;pp<(p)->last;)\
+				*pp++='\0'
+#define OUTC(x)		{Bputc(&bout,x); if(--count == 0){Bprint(&bout,"\\\n"); count=ll;} }
+#define TEST2		{if((count -= 2) <=0){Bprint(&bout,"\\\n");count=ll;}}
+#define EMPTY		if(stkerr != 0){Bprint(&bout,"stack empty\n"); continue; }
+#define EMPTYR(x)	if(stkerr!=0){pushp(x);Bprint(&bout,"stack empty\n");continue;}
+#define EMPTYS		if(stkerr != 0){Bprint(&bout,"stack empty\n"); return(1);}
+#define EMPTYSR(x)	if(stkerr !=0){Bprint(&bout,"stack empty\n");pushp(x);return(1);}
+#define error(p)	{Bprint(&bout,p); continue; }
+#define errorrt(p)	{Bprint(&bout,p); return(1); }
+#define LASTFUN 026
+
+typedef	struct	Blk	Blk;
+struct	Blk
+{
+	char	*rd;
+	char	*wt;
+	char	*beg;
+	char	*last;
+};
+typedef	struct	Sym	Sym;
+struct	Sym
+{
+	Sym	*next;
+	Blk	*val;
+};
+typedef	struct	Wblk	Wblk;
+struct	Wblk
+{
+	Blk	**rdw;
+	Blk	**wtw;
+	Blk	**begw;
+	Blk	**lastw;
+};
+
+Biobuf	*curfile, *fsave;
+Blk	*arg1, *arg2;
+uchar	savk;
+int	dbg;
+int	ifile;
+Blk	*scalptr, *basptr, *tenptr, *inbas;
+Blk	*sqtemp, *chptr, *strptr, *divxyz;
+Blk	*stack[STKSZ];
+Blk	**stkptr,**stkbeg;
+Blk	**stkend;
+Blk	*hfree;
+int	stkerr;
+int	lastchar;
+Blk	*readstk[RDSKSZ];
+Blk	**readptr;
+Blk	*rem;
+int	k;
+Blk	*irem;
+int	skd,skr;
+int	neg;
+Sym	symlst[TBLSZ];
+Sym	*stable[TBLSZ];
+Sym	*sptr, *sfree;
+long	rel;
+long	nbytes;
+long	all;
+long	headmor;
+long	obase;
+int	fw,fw1,ll;
+void	(*outdit)(Blk *p, int flg);
+int	logo;
+int	logten;
+int	count;
+char	*pp;
+char	*dummy;
+long	longest, maxsize, active;
+int	lall, lrel, lcopy, lmore, lbytes;
+int	inside;
+Biobuf	bin;
+Biobuf	bout;
+
+void	main(int argc, char *argv[]);
+void	commnds(void);
+Blk*	readin(void);
+Blk*	div(Blk *ddivd, Blk *ddivr);
+int	dscale(void);
+Blk*	removr(Blk *p, int n);
+Blk*	dcsqrt(Blk *p);
+void	init(int argc, char *argv[]);
+void	onintr(void);
+void	pushp(Blk *p);
+Blk*	pop(void);
+Blk*	readin(void);
+Blk*	add0(Blk *p, int ct);
+Blk*	mult(Blk *p, Blk *q);
+void	chsign(Blk *p);
+int	readc(void);
+void	unreadc(char c);
+void	binop(char c);
+void	dcprint(Blk *hptr);
+Blk*	dcexp(Blk *base, Blk *ex);
+Blk*	getdec(Blk *p, int sc);
+void	tenot(Blk *p, int sc);
+void	oneot(Blk *p, int sc, char ch);
+void	hexot(Blk *p, int flg);
+void	bigot(Blk *p, int flg);
+Blk*	add(Blk *a1, Blk *a2);
+int	eqk(void);
+Blk*	removc(Blk *p, int n);
+Blk*	scalint(Blk *p);
+Blk*	scale(Blk *p, int n);
+int	subt(void);
+int	command(void);
+int	cond(char c);
+void	load(void);
+int	log2(long n);
+Blk*	salloc(int size);
+Blk*	morehd(void);
+Blk*	copy(Blk *hptr, int size);
+void	sdump(char *s1, Blk *hptr);
+void	seekc(Blk *hptr, int n);
+void	salterwd(Blk *hptr, Blk *n);
+void	more(Blk *hptr);
+void	ospace(char *s);
+void	garbage(char *s);
+void	release(Blk *p);
+Blk*	dcgetwd(Blk *p);
+void	putwd(Blk *p, Blk *c);
+Blk*	lookwd(Blk *p);
+char*	nalloc(char *p, unsigned nbytes);
+int	getstk(void);
+
+/********debug only**/
+void
+tpr(char *cp, Blk *bp)
+{
+	print("%s-> ", cp);
+	print("beg: %lx rd: %lx wt: %lx last: %lx\n", bp->beg, bp->rd,
+		bp->wt, bp->last);
+	for (cp = bp->beg; cp != bp->wt; cp++) {
+		print("%d", *cp);
+		if (cp != bp->wt-1)
+			print("/");
+	}
+	print("\n");
+}
+/************/
+
+void
+main(int argc, char *argv[])
+{
+	Binit(&bin, 0, OREAD);
+	Binit(&bout, 1, OWRITE);
+	init(argc,argv);
+	commnds();
+	exits(0);
+}
+
+void
+commnds(void)
+{
+	Blk *p, *q, **ptr, *s, *t;
+	long l;
+	Sym *sp;
+	int sk, sk1, sk2, c, sign, n, d;
+
+	while(1) {
+		Bflush(&bout);
+		if(((c = readc())>='0' && c <= '9') ||
+		    (c>='A' && c <='F') || c == '.') {
+			unreadc(c);
+			p = readin();
+			pushp(p);
+			continue;
+		}
+		switch(c) {
+		case ' ':
+		case '\n':
+		case -1:
+			continue;
+		case 'Y':
+			sdump("stk",*stkptr);
+			Bprint(&bout, "all %ld rel %ld headmor %ld\n",all,rel,headmor);
+			Bprint(&bout, "nbytes %ld\n",nbytes);
+			Bprint(&bout, "longest %ld active %ld maxsize %ld\n", longest,
+				active, maxsize);
+			Bprint(&bout, "new all %d rel %d copy %d more %d lbytes %d\n",
+				lall, lrel, lcopy, lmore, lbytes);
+			lall = lrel = lcopy = lmore = lbytes = 0;
+			continue;
+		case '_':
+			p = readin();
+			savk = sunputc(p);
+			chsign(p);
+			sputc(p,savk);
+			pushp(p);
+			continue;
+		case '-':
+			subt();
+			continue;
+		case '+':
+			if(eqk() != 0)
+				continue;
+			binop('+');
+			continue;
+		case '*':
+			arg1 = pop();
+			EMPTY;
+			arg2 = pop();
+			EMPTYR(arg1);
+			sk1 = sunputc(arg1);
+			sk2 = sunputc(arg2);
+			savk = sk1+sk2;
+			binop('*');
+			p = pop();
+			if(savk>k && savk>sk1 && savk>sk2) {
+				sclobber(p);
+				sk = sk1;
+				if(sk<sk2)
+					sk = sk2;
+				if(sk<k)
+					sk = k;
+				p = removc(p,savk-sk);
+				savk = sk;
+				sputc(p,savk);
+			}
+			pushp(p);
+			continue;
+		case '/':
+		casediv:
+			if(dscale() != 0)
+				continue;
+			binop('/');
+			if(irem != 0)
+				release(irem);
+			release(rem);
+			continue;
+		case '%':
+			if(dscale() != 0)
+				continue;
+			binop('/');
+			p = pop();
+			release(p);
+			if(irem == 0) {
+				sputc(rem,skr+k);
+				pushp(rem);
+				continue;
+			}
+			p = add0(rem,skd-(skr+k));
+			q = add(p,irem);
+			release(p);
+			release(irem);
+			sputc(q,skd);
+			pushp(q);
+			continue;
+		case 'v':
+			p = pop();
+			EMPTY;
+			savk = sunputc(p);
+			if(length(p) == 0) {
+				sputc(p,savk);
+				pushp(p);
+				continue;
+			}
+			if(sbackc(p)<0) {
+				error("sqrt of neg number\n");
+			}
+			if(k<savk)
+				n = savk;
+			else {
+				n = k*2-savk;
+				savk = k;
+			}
+			arg1 = add0(p,n);
+			arg2 = dcsqrt(arg1);
+			sputc(arg2,savk);
+			pushp(arg2);
+			continue;
+
+		case '^':
+			neg = 0;
+			arg1 = pop();
+			EMPTY;
+			if(sunputc(arg1) != 0)
+				error("exp not an integer\n");
+			arg2 = pop();
+			EMPTYR(arg1);
+			if(sfbeg(arg1) == 0 && sbackc(arg1)<0) {
+				neg++;
+				chsign(arg1);
+			}
+			if(length(arg1)>=3) {
+				error("exp too big\n");
+			}
+			savk = sunputc(arg2);
+			p = dcexp(arg2,arg1);
+			release(arg2);
+			rewind(arg1);
+			c = sgetc(arg1);
+			if(c == -1)
+				c = 0;
+			else
+			if(sfeof(arg1) == 0)
+				c = sgetc(arg1)*100 + c;
+			d = c*savk;
+			release(arg1);
+		/*	if(neg == 0) {		removed to fix -exp bug*/
+				if(k>=savk)
+					n = k;
+				else
+					n = savk;
+				if(n<d) {
+					q = removc(p,d-n);
+					sputc(q,n);
+					pushp(q);
+				} else {
+					sputc(p,d);
+					pushp(p);
+				}
+		/*	} else { this is disaster for exp <-127 */
+		/*		sputc(p,d);		*/
+		/*		pushp(p);		*/
+		/*	}				*/
+			if(neg == 0)
+				continue;
+			p = pop();
+			q = salloc(2);
+			sputc(q,1);
+			sputc(q,0);
+			pushp(q);
+			pushp(p);
+			goto casediv;
+		case 'z':
+			p = salloc(2);
+			n = stkptr - stkbeg;
+			if(n >= 100) {
+				sputc(p,n/100);
+				n %= 100;
+			}
+			sputc(p,n);
+			sputc(p,0);
+			pushp(p);
+			continue;
+		case 'Z':
+			p = pop();
+			EMPTY;
+			n = (length(p)-1)<<1;
+			fsfile(p);
+			backc(p);
+			if(sfbeg(p) == 0) {
+				if((c = sbackc(p))<0) {
+					n -= 2;
+					if(sfbeg(p) == 1)
+						n++;
+					else {
+						if((c = sbackc(p)) == 0)
+							n++;
+						else
+						if(c > 90)
+							n--;
+					}
+				} else
+				if(c < 10)
+					n--;
+			}
+			release(p);
+			q = salloc(1);
+			if(n >= 100) {
+				sputc(q,n%100);
+				n /= 100;
+			}
+			sputc(q,n);
+			sputc(q,0);
+			pushp(q);
+			continue;
+		case 'i':
+			p = pop();
+			EMPTY;
+			p = scalint(p);
+			release(inbas);
+			inbas = p;
+			continue;
+		case 'I':
+			p = copy(inbas,length(inbas)+1);
+			sputc(p,0);
+			pushp(p);
+			continue;
+		case 'o':
+			p = pop();
+			EMPTY;
+			p = scalint(p);
+			sign = 0;
+			n = length(p);
+			q = copy(p,n);
+			fsfile(q);
+			l = c = sbackc(q);
+			if(n != 1) {
+				if(c<0) {
+					sign = 1;
+					chsign(q);
+					n = length(q);
+					fsfile(q);
+					l = c = sbackc(q);
+				}
+				if(n != 1) {
+					while(sfbeg(q) == 0)
+						l = l*100+sbackc(q);
+				}
+			}
+			logo = log2(l);
+			obase = l;
+			release(basptr);
+			if(sign == 1)
+				obase = -l;
+			basptr = p;
+			outdit = bigot;
+			if(n == 1 && sign == 0) {
+				if(c <= 16) {
+					outdit = hexot;
+					fw = 1;
+					fw1 = 0;
+					ll = 70;
+					release(q);
+					continue;
+				}
+			}
+			n = 0;
+			if(sign == 1)
+				n++;
+			p = salloc(1);
+			sputc(p,-1);
+			t = add(p,q);
+			n += length(t)*2;
+			fsfile(t);
+			if(sbackc(t)>9)
+				n++;
+			release(t);
+			release(q);
+			release(p);
+			fw = n;
+			fw1 = n-1;
+			ll = 70;
+			if(fw>=ll)
+				continue;
+			ll = (70/fw)*fw;
+			continue;
+		case 'O':
+			p = copy(basptr,length(basptr)+1);
+			sputc(p,0);
+			pushp(p);
+			continue;
+		case '[':
+			n = 0;
+			p = salloc(0);
+			for(;;) {
+				if((c = readc()) == ']') {
+					if(n == 0)
+						break;
+					n--;
+				}
+				sputc(p,c);
+				if(c == '[')
+					n++;
+			}
+			pushp(p);
+			continue;
+		case 'k':
+			p = pop();
+			EMPTY;
+			p = scalint(p);
+			if(length(p)>1) {
+				error("scale too big\n");
+			}
+			rewind(p);
+			k = 0;
+			if(!sfeof(p))
+				k = sgetc(p);
+			release(scalptr);
+			scalptr = p;
+			continue;
+		case 'K':
+			p = copy(scalptr,length(scalptr)+1);
+			sputc(p,0);
+			pushp(p);
+			continue;
+		case 'X':
+			p = pop();
+			EMPTY;
+			fsfile(p);
+			n = sbackc(p);
+			release(p);
+			p = salloc(2);
+			sputc(p,n);
+			sputc(p,0);
+			pushp(p);
+			continue;
+		case 'Q':
+			p = pop();
+			EMPTY;
+			if(length(p)>2) {
+				error("Q?\n");
+			}
+			rewind(p);
+			if((c =  sgetc(p))<0) {
+				error("neg Q\n");
+			}
+			release(p);
+			while(c-- > 0) {
+				if(readptr == &readstk[0]) {
+					error("readstk?\n");
+				}
+				if(*readptr != 0)
+					release(*readptr);
+				readptr--;
+			}
+			continue;
+		case 'q':
+			if(readptr <= &readstk[1])
+				exits(0);
+			if(*readptr != 0)
+				release(*readptr);
+			readptr--;
+			if(*readptr != 0)
+				release(*readptr);
+			readptr--;
+			continue;
+		case 'f':
+			if(stkptr == &stack[0])
+				Bprint(&bout,"empty stack\n");
+			else {
+				for(ptr = stkptr; ptr > &stack[0];) {
+					dcprint(*ptr--);
+				}
+			}
+			continue;
+		case 'p':
+			if(stkptr == &stack[0])
+				Bprint(&bout,"empty stack\n");
+			else {
+				dcprint(*stkptr);
+			}
+			continue;
+		case 'P':
+			p = pop();
+			EMPTY;
+			sputc(p,0);
+			Bprint(&bout,"%s",p->beg);
+			release(p);
+			continue;
+		case 'd':
+			if(stkptr == &stack[0]) {
+				Bprint(&bout,"empty stack\n");
+				continue;
+			}
+			q = *stkptr;
+			n = length(q);
+			p = copy(*stkptr,n);
+			pushp(p);
+			continue;
+		case 'c':
+			while(stkerr == 0) {
+				p = pop();
+				if(stkerr == 0)
+					release(p);
+			}
+			continue;
+		case 'S':
+			if(stkptr == &stack[0]) {
+				error("save: args\n");
+			}
+			c = getstk() & 0377;
+			sptr = stable[c];
+			sp = stable[c] = sfree;
+			sfree = sfree->next;
+			if(sfree == 0)
+				goto sempty;
+			sp->next = sptr;
+			p = pop();
+			EMPTY;
+			if(c >= ARRAYST) {
+				q = copy(p,length(p)+PTRSZ);
+				for(n = 0;n < PTRSZ;n++) {
+					sputc(q,0);
+				}
+				release(p);
+				p = q;
+			}
+			sp->val = p;
+			continue;
+		sempty:
+			error("symbol table overflow\n");
+		case 's':
+			if(stkptr == &stack[0]) {
+				error("save:args\n");
+			}
+			c = getstk() & 0377;
+			sptr = stable[c];
+			if(sptr != 0) {
+				p = sptr->val;
+				if(c >= ARRAYST) {
+					rewind(p);
+					while(sfeof(p) == 0)
+						release(dcgetwd(p));
+				}
+				release(p);
+			} else {
+				sptr = stable[c] = sfree;
+				sfree = sfree->next;
+				if(sfree == 0)
+					goto sempty;
+				sptr->next = 0;
+			}
+			p = pop();
+			sptr->val = p;
+			continue;
+		case 'l':
+			load();
+			continue;
+		case 'L':
+			c = getstk() & 0377;
+			sptr = stable[c];
+			if(sptr == 0) {
+				error("L?\n");
+			}
+			stable[c] = sptr->next;
+			sptr->next = sfree;
+			sfree = sptr;
+			p = sptr->val;
+			if(c >= ARRAYST) {
+				rewind(p);
+				while(sfeof(p) == 0) {
+					q = dcgetwd(p);
+					if(q != 0)
+						release(q);
+				}
+			}
+			pushp(p);
+			continue;
+		case ':':
+			p = pop();
+			EMPTY;
+			q = scalint(p);
+			fsfile(q);
+			c = 0;
+			if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+				error("neg index\n");
+			}
+			if(length(q)>2) {
+				error("index too big\n");
+			}
+			if(sfbeg(q) == 0)
+				c = c*100+sbackc(q);
+			if(c >= MAXIND) {
+				error("index too big\n");
+			}
+			release(q);
+			n = getstk() & 0377;
+			sptr = stable[n];
+			if(sptr == 0) {
+				sptr = stable[n] = sfree;
+				sfree = sfree->next;
+				if(sfree == 0)
+					goto sempty;
+				sptr->next = 0;
+				p = salloc((c+PTRSZ)*PTRSZ);
+				zero(p);
+			} else {
+				p = sptr->val;
+				if(length(p)-PTRSZ < c*PTRSZ) {
+					q = copy(p,(c+PTRSZ)*PTRSZ);
+					release(p);
+					p = q;
+				}
+			}
+			seekc(p,c*PTRSZ);
+			q = lookwd(p);
+			if(q!=0)
+				release(q);
+			s = pop();
+			EMPTY;
+			salterwd(p, s);
+			sptr->val = p;
+			continue;
+		case ';':
+			p = pop();
+			EMPTY;
+			q = scalint(p);
+			fsfile(q);
+			c = 0;
+			if((sfbeg(q) == 0) && ((c = sbackc(q))<0)) {
+				error("neg index\n");
+			}
+			if(length(q)>2) {
+				error("index too big\n");
+			}
+			if(sfbeg(q) == 0)
+				c = c*100+sbackc(q);
+			if(c >= MAXIND) {
+				error("index too big\n");
+			}
+			release(q);
+			n = getstk() & 0377;
+			sptr = stable[n];
+			if(sptr != 0){
+				p = sptr->val;
+				if(length(p)-PTRSZ >= c*PTRSZ) {
+					seekc(p,c*PTRSZ);
+					s = dcgetwd(p);
+					if(s != 0) {
+						q = copy(s,length(s));
+						pushp(q);
+						continue;
+					}
+				}
+			}
+			q = salloc(1);	/*so uninitialized array elt prints as 0*/
+			sputc(q, 0);
+			pushp(q);
+			continue;
+		case 'x':
+		execute:
+			p = pop();
+			EMPTY;
+			if((readptr != &readstk[0]) && (*readptr != 0)) {
+				if((*readptr)->rd == (*readptr)->wt)
+					release(*readptr);
+				else {
+					if(readptr++ == &readstk[RDSKSZ]) {
+						error("nesting depth\n");
+					}
+				}
+			} else
+				readptr++;
+			*readptr = p;
+			if(p != 0)
+				rewind(p);
+			else {
+				if((c = readc()) != '\n')
+					unreadc(c);
+			}
+			continue;
+		case '?':
+			if(++readptr == &readstk[RDSKSZ]) {
+				error("nesting depth\n");
+			}
+			*readptr = 0;
+			fsave = curfile;
+			curfile = &bin;
+			while((c = readc()) == '!')
+				command();
+			p = salloc(0);
+			sputc(p,c);
+			while((c = readc()) != '\n') {
+				sputc(p,c);
+				if(c == '\\')
+					sputc(p,readc());
+			}
+			curfile = fsave;
+			*readptr = p;
+			continue;
+		case '!':
+			if(command() == 1)
+				goto execute;
+			continue;
+		case '<':
+		case '>':
+		case '=':
+			if(cond(c) == 1)
+				goto execute;
+			continue;
+		default:
+			Bprint(&bout,"%o is unimplemented\n",c);
+		}
+	}
+}
+
+Blk*
+div(Blk *ddivd, Blk *ddivr)
+{
+	int divsign, remsign, offset, divcarry,
+		carry, dig, magic, d, dd, under, first;
+	long c, td, cc;
+	Blk *ps, *px, *p, *divd, *divr;
+
+	dig = 0;
+	under = 0;
+	divcarry = 0;
+	rem = 0;
+	p = salloc(0);
+	if(length(ddivr) == 0) {
+		pushp(ddivr);
+		Bprint(&bout,"divide by 0\n");
+		return(p);
+	}
+	divsign = remsign = first = 0;
+	divr = ddivr;
+	fsfile(divr);
+	if(sbackc(divr) == -1) {
+		divr = copy(ddivr,length(ddivr));
+		chsign(divr);
+		divsign = ~divsign;
+	}
+	divd = copy(ddivd,length(ddivd));
+	fsfile(divd);
+	if(sfbeg(divd) == 0 && sbackc(divd) == -1) {
+		chsign(divd);
+		divsign = ~divsign;
+		remsign = ~remsign;
+	}
+	offset = length(divd) - length(divr);
+	if(offset < 0)
+		goto ddone;
+	seekc(p,offset+1);
+	sputc(divd,0);
+	magic = 0;
+	fsfile(divr);
+	c = sbackc(divr);
+	if(c < 10)
+		magic++;
+	c = c * 100 + (sfbeg(divr)?0:sbackc(divr));
+	if(magic>0){
+		c = (c * 100 +(sfbeg(divr)?0:sbackc(divr)))*2;
+		c /= 25;
+	}
+	while(offset >= 0) {
+		first++;
+		fsfile(divd);
+		td = sbackc(divd) * 100;
+		dd = sfbeg(divd)?0:sbackc(divd);
+		td = (td + dd) * 100;
+		dd = sfbeg(divd)?0:sbackc(divd);
+		td = td + dd;
+		cc = c;
+		if(offset == 0)
+			td++;
+		else
+			cc++;
+		if(magic != 0)
+			td = td<<3;
+		dig = td/cc;
+		under=0;
+		if(td%cc < 8  && dig > 0 && magic) {
+			dig--;
+			under=1;
+		}
+		rewind(divr);
+		rewind(divxyz);
+		carry = 0;
+		while(sfeof(divr) == 0) {
+			d = sgetc(divr)*dig+carry;
+			carry = d / 100;
+			salterc(divxyz,d%100);
+		}
+		salterc(divxyz,carry);
+		rewind(divxyz);
+		seekc(divd,offset);
+		carry = 0;
+		while(sfeof(divd) == 0) {
+			d = slookc(divd);
+			d = d-(sfeof(divxyz)?0:sgetc(divxyz))-carry;
+			carry = 0;
+			if(d < 0) {
+				d += 100;
+				carry = 1;
+			}
+			salterc(divd,d);
+		}
+		divcarry = carry;
+		backc(p);
+		salterc(p,dig);
+		backc(p);
+		fsfile(divd);
+		d=sbackc(divd);
+		if((d != 0) && /*!divcarry*/ (offset != 0)) {
+			d = sbackc(divd) + 100;
+			salterc(divd,d);
+		}
+		if(--offset >= 0)
+			divd->wt--;
+	}
+	if(under) {	/* undershot last - adjust*/
+		px = copy(divr,length(divr));	/*11/88 don't corrupt ddivr*/
+		chsign(px);
+		ps = add(px,divd);
+		fsfile(ps);
+		if(length(ps) > 0 && sbackc(ps) < 0) {
+			release(ps);	/*only adjust in really undershot*/
+		} else {
+			release(divd);
+			salterc(p, dig+1);
+			divd=ps;
+		}
+	}
+	if(divcarry != 0) {
+		salterc(p,dig-1);
+		salterc(divd,-1);
+		ps = add(divr,divd);
+		release(divd);
+		divd = ps;
+	}
+
+	rewind(p);
+	divcarry = 0;
+	while(sfeof(p) == 0){
+		d = slookc(p)+divcarry;
+		divcarry = 0;
+		if(d >= 100){
+			d -= 100;
+			divcarry = 1;
+		}
+		salterc(p,d);
+	}
+	if(divcarry != 0)salterc(p,divcarry);
+	fsfile(p);
+	while(sfbeg(p) == 0) {
+		if(sbackc(p) != 0)
+			break;
+		truncate(p);
+	}
+	if(divsign < 0)
+		chsign(p);
+	fsfile(divd);
+	while(sfbeg(divd) == 0) {
+		if(sbackc(divd) != 0)
+			break;
+		truncate(divd);
+	}
+ddone:
+	if(remsign<0)
+		chsign(divd);
+	if(divr != ddivr)
+		release(divr);
+	rem = divd;
+	return(p);
+}
+
+int
+dscale(void)
+{
+	Blk *dd, *dr, *r;
+	int c;
+
+	dr = pop();
+	EMPTYS;
+	dd = pop();
+	EMPTYSR(dr);
+	fsfile(dd);
+	skd = sunputc(dd);
+	fsfile(dr);
+	skr = sunputc(dr);
+	if(sfbeg(dr) == 1 || (sfbeg(dr) == 0 && sbackc(dr) == 0)) {
+		sputc(dr,skr);
+		pushp(dr);
+		Bprint(&bout,"divide by 0\n");
+		return(1);
+	}
+	if(sfbeg(dd) == 1 || (sfbeg(dd) == 0 && sbackc(dd) == 0)) {
+		sputc(dd,skd);
+		pushp(dd);
+		return(1);
+	}
+	c = k-skd+skr;
+	if(c < 0)
+		r = removr(dd,-c);
+	else {
+		r = add0(dd,c);
+		irem = 0;
+	}
+	arg1 = r;
+	arg2 = dr;
+	savk = k;
+	return(0);
+}
+
+Blk*
+removr(Blk *p, int n)
+{
+	int nn, neg;
+	Blk *q, *s, *r;
+
+	fsfile(p);
+	neg = sbackc(p);
+	if(neg < 0)
+		chsign(p);
+	rewind(p);
+	nn = (n+1)/2;
+	q = salloc(nn);
+	while(n>1) {
+		sputc(q,sgetc(p));
+		n -= 2;
+	}
+	r = salloc(2);
+	while(sfeof(p) == 0)
+		sputc(r,sgetc(p));
+	release(p);
+	if(n == 1){
+		s = div(r,tenptr);
+		release(r);
+		rewind(rem);
+		if(sfeof(rem) == 0)
+			sputc(q,sgetc(rem));
+		release(rem);
+		if(neg < 0){
+			chsign(s);
+			chsign(q);
+			irem = q;
+			return(s);
+		}
+		irem = q;
+		return(s);
+	}
+	if(neg < 0) {
+		chsign(r);
+		chsign(q);
+		irem = q;
+		return(r);
+	}
+	irem = q;
+	return(r);
+}
+
+Blk*
+dcsqrt(Blk *p)
+{
+	Blk *t, *r, *q, *s;
+	int c, n, nn;
+
+	n = length(p);
+	fsfile(p);
+	c = sbackc(p);
+	if((n&1) != 1)
+		c = c*100+(sfbeg(p)?0:sbackc(p));
+	n = (n+1)>>1;
+	r = salloc(n);
+	zero(r);
+	seekc(r,n);
+	nn=1;
+	while((c -= nn)>=0)
+		nn+=2;
+	c=(nn+1)>>1;
+	fsfile(r);
+	backc(r);
+	if(c>=100) {
+		c -= 100;
+		salterc(r,c);
+		sputc(r,1);
+	} else
+		salterc(r,c);
+	for(;;){
+		q = div(p,r);
+		s = add(q,r);
+		release(q);
+		release(rem);
+		q = div(s,sqtemp);
+		release(s);
+		release(rem);
+		s = copy(r,length(r));
+		chsign(s);
+		t = add(s,q);
+		release(s);
+		fsfile(t);
+		nn = sfbeg(t)?0:sbackc(t);
+		if(nn>=0)
+			break;
+		release(r);
+		release(t);
+		r = q;
+	}
+	release(t);
+	release(q);
+	release(p);
+	return(r);
+}
+
+Blk*
+dcexp(Blk *base, Blk *ex)
+{
+	Blk *r, *e, *p, *e1, *t, *cp;
+	int temp, c, n;
+
+	r = salloc(1);
+	sputc(r,1);
+	p = copy(base,length(base));
+	e = copy(ex,length(ex));
+	fsfile(e);
+	if(sfbeg(e) != 0)
+		goto edone;
+	temp=0;
+	c = sbackc(e);
+	if(c<0) {
+		temp++;
+		chsign(e);
+	}
+	while(length(e) != 0) {
+		e1=div(e,sqtemp);
+		release(e);
+		e = e1;
+		n = length(rem);
+		release(rem);
+		if(n != 0) {
+			e1=mult(p,r);
+			release(r);
+			r = e1;
+		}
+		t = copy(p,length(p));
+		cp = mult(p,t);
+		release(p);
+		release(t);
+		p = cp;
+	}
+	if(temp != 0) {
+		if((c = length(base)) == 0) {
+			goto edone;
+		}
+		if(c>1)
+			create(r);
+		else {
+			rewind(base);
+			if((c = sgetc(base))<=1) {
+				create(r);
+				sputc(r,c);
+			} else
+				create(r);
+		}
+	}
+edone:
+	release(p);
+	release(e);
+	return(r);
+}
+
+void
+init(int argc, char *argv[])
+{
+	Sym *sp;
+	Dir *d;
+
+	ARGBEGIN {
+	default:
+		dbg = 1;
+		break;
+	} ARGEND
+	ifile = 1;
+	curfile = &bin;
+	if(*argv){
+		d = dirstat(*argv);
+		if(d == nil) {
+			fprint(2, "dc: can't open file %s\n", *argv);
+			exits("open");
+		}
+		if(d->mode & DMDIR) {
+			fprint(2, "dc: file %s is a directory\n", *argv);
+			exits("open");
+		}
+		free(d);
+		if((curfile = Bopen(*argv, OREAD)) == 0) {
+			fprint(2,"dc: can't open file %s\n", *argv);
+			exits("open");
+		}
+	}
+/*	dummy = malloc(0);  *//* prepare for garbage-collection */
+	scalptr = salloc(1);
+	sputc(scalptr,0);
+	basptr = salloc(1);
+	sputc(basptr,10);
+	obase=10;
+	logten=log2(10L);
+	ll=70;
+	fw=1;
+	fw1=0;
+	tenptr = salloc(1);
+	sputc(tenptr,10);
+	obase=10;
+	inbas = salloc(1);
+	sputc(inbas,10);
+	sqtemp = salloc(1);
+	sputc(sqtemp,2);
+	chptr = salloc(0);
+	strptr = salloc(0);
+	divxyz = salloc(0);
+	stkbeg = stkptr = &stack[0];
+	stkend = &stack[STKSZ];
+	stkerr = 0;
+	readptr = &readstk[0];
+	k=0;
+	sp = sptr = &symlst[0];
+	while(sptr < &symlst[TBLSZ]) {
+		sptr->next = ++sp;
+		sptr++;
+	}
+	sptr->next=0;
+	sfree = &symlst[0];
+}
+
+void
+pushp(Blk *p)
+{
+	if(stkptr == stkend) {
+		Bprint(&bout,"out of stack space\n");
+		return;
+	}
+	stkerr=0;
+	*++stkptr = p;
+	return;
+}
+
+Blk*
+pop(void)
+{
+	if(stkptr == stack) {
+		stkerr=1;
+		return(0);
+	}
+	return(*stkptr--);
+}
+
+Blk*
+readin(void)
+{
+	Blk *p, *q;
+	int dp, dpct, c;
+
+	dp = dpct=0;
+	p = salloc(0);
+	for(;;){
+		c = readc();
+		switch(c) {
+		case '.':
+			if(dp != 0)
+				goto gotnum;
+			dp++;
+			continue;
+		case '\\':
+			readc();
+			continue;
+		default:
+			if(c >= 'A' && c <= 'F')
+				c = c - 'A' + 10;
+			else
+			if(c >= '0' && c <= '9')
+				c -= '0';
+			else
+				goto gotnum;
+			if(dp != 0) {
+				if(dpct >= 99)
+					continue;
+				dpct++;
+			}
+			create(chptr);
+			if(c != 0)
+				sputc(chptr,c);
+			q = mult(p,inbas);
+			release(p);
+			p = add(chptr,q);
+			release(q);
+		}
+	}
+gotnum:
+	unreadc(c);
+	if(dp == 0) {
+		sputc(p,0);
+		return(p);
+	} else {
+		q = scale(p,dpct);
+		return(q);
+	}
+}
+
+/*
+ * returns pointer to struct with ct 0's & p
+ */
+Blk*
+add0(Blk *p, int ct)
+{
+	Blk *q, *t;
+
+	q = salloc(length(p)+(ct+1)/2);
+	while(ct>1) {
+		sputc(q,0);
+		ct -= 2;
+	}
+	rewind(p);
+	while(sfeof(p) == 0) {
+		sputc(q,sgetc(p));
+	}
+	release(p);
+	if(ct == 1) {
+		t = mult(tenptr,q);
+		release(q);
+		return(t);
+	}
+	return(q);
+}
+
+Blk*
+mult(Blk *p, Blk *q)
+{
+	Blk *mp, *mq, *mr;
+	int sign, offset, carry;
+	int cq, cp, mt, mcr;
+
+	offset = sign = 0;
+	fsfile(p);
+	mp = p;
+	if(sfbeg(p) == 0) {
+		if(sbackc(p)<0) {
+			mp = copy(p,length(p));
+			chsign(mp);
+			sign = ~sign;
+		}
+	}
+	fsfile(q);
+	mq = q;
+	if(sfbeg(q) == 0){
+		if(sbackc(q)<0) {
+			mq = copy(q,length(q));
+			chsign(mq);
+			sign = ~sign;
+		}
+	}
+	mr = salloc(length(mp)+length(mq));
+	zero(mr);
+	rewind(mq);
+	while(sfeof(mq) == 0) {
+		cq = sgetc(mq);
+		rewind(mp);
+		rewind(mr);
+		mr->rd += offset;
+		carry=0;
+		while(sfeof(mp) == 0) {
+			cp = sgetc(mp);
+			mcr = sfeof(mr)?0:slookc(mr);
+			mt = cp*cq + carry + mcr;
+			carry = mt/100;
+			salterc(mr,mt%100);
+		}
+		offset++;
+		if(carry != 0) {
+			mcr = sfeof(mr)?0:slookc(mr);
+			salterc(mr,mcr+carry);
+		}
+	}
+	if(sign < 0) {
+		chsign(mr);
+	}
+	if(mp != p)
+		release(mp);
+	if(mq != q)
+		release(mq);
+	return(mr);
+}
+
+void
+chsign(Blk *p)
+{
+	int carry;
+	char ct;
+
+	carry=0;
+	rewind(p);
+	while(sfeof(p) == 0) {
+		ct=100-slookc(p)-carry;
+		carry=1;
+		if(ct>=100) {
+			ct -= 100;
+			carry=0;
+		}
+		salterc(p,ct);
+	}
+	if(carry != 0) {
+		sputc(p,-1);
+		fsfile(p);
+		backc(p);
+		ct = sbackc(p);
+		if(ct == 99 /*&& !sfbeg(p)*/) {
+			truncate(p);
+			sputc(p,-1);
+		}
+	} else{
+		fsfile(p);
+		ct = sbackc(p);
+		if(ct == 0)
+			truncate(p);
+	}
+	return;
+}
+
+int
+readc(void)
+{
+loop:
+	if((readptr != &readstk[0]) && (*readptr != 0)) {
+		if(sfeof(*readptr) == 0)
+			return(lastchar = sgetc(*readptr));
+		release(*readptr);
+		readptr--;
+		goto loop;
+	}
+	lastchar = Bgetc(curfile);
+	if(lastchar != -1)
+		return(lastchar);
+	if(readptr != &readptr[0]) {
+		readptr--;
+		if(*readptr == 0)
+			curfile = &bin;
+		goto loop;
+	}
+	if(curfile != &bin) {
+		Bterm(curfile);
+		curfile = &bin;
+		goto loop;
+	}
+	exits(0);
+	return 0;	/* shut up ken */
+}
+
+void
+unreadc(char c)
+{
+
+	if((readptr != &readstk[0]) && (*readptr != 0)) {
+		sungetc(*readptr,c);
+	} else
+		Bungetc(curfile);
+	return;
+}
+
+void
+binop(char c)
+{
+	Blk *r;
+
+	r = 0;
+	switch(c) {
+	case '+':
+		r = add(arg1,arg2);
+		break;
+	case '*':
+		r = mult(arg1,arg2);
+		break;
+	case '/':
+		r = div(arg1,arg2);
+		break;
+	}
+	release(arg1);
+	release(arg2);
+	sputc(r,savk);
+	pushp(r);
+}
+
+void
+dcprint(Blk *hptr)
+{
+	Blk *p, *q, *dec;
+	int dig, dout, ct, sc;
+
+	rewind(hptr);
+	while(sfeof(hptr) == 0) {
+		if(sgetc(hptr)>99) {
+			rewind(hptr);
+			while(sfeof(hptr) == 0) {
+				Bprint(&bout,"%c",sgetc(hptr));
+			}
+			Bprint(&bout,"\n");
+			return;
+		}
+	}
+	fsfile(hptr);
+	sc = sbackc(hptr);
+	if(sfbeg(hptr) != 0) {
+		Bprint(&bout,"0\n");
+		return;
+	}
+	count = ll;
+	p = copy(hptr,length(hptr));
+	sclobber(p);
+	fsfile(p);
+	if(sbackc(p)<0) {
+		chsign(p);
+		OUTC('-');
+	}
+	if((obase == 0) || (obase == -1)) {
+		oneot(p,sc,'d');
+		return;
+	}
+	if(obase == 1) {
+		oneot(p,sc,'1');
+		return;
+	}
+	if(obase == 10) {
+		tenot(p,sc);
+		return;
+	}
+	/* sleazy hack to scale top of stack - divide by 1 */
+	pushp(p);
+	sputc(p, sc);
+	p=salloc(0);
+	create(p);
+	sputc(p, 1);
+	sputc(p, 0);
+	pushp(p);
+	if(dscale() != 0)
+		return;
+	p = div(arg1, arg2);
+	release(arg1);
+	release(arg2);
+	sc = savk;
+
+	create(strptr);
+	dig = logten*sc;
+	dout = ((dig/10) + dig) / logo;
+	dec = getdec(p,sc);
+	p = removc(p,sc);
+	while(length(p) != 0) {
+		q = div(p,basptr);
+		release(p);
+		p = q;
+		(*outdit)(rem,0);
+	}
+	release(p);
+	fsfile(strptr);
+	while(sfbeg(strptr) == 0)
+		OUTC(sbackc(strptr));
+	if(sc == 0) {
+		release(dec);
+		Bprint(&bout,"\n");
+		return;
+	}
+	create(strptr);
+	OUTC('.');
+	ct=0;
+	do {
+		q = mult(basptr,dec);
+		release(dec);
+		dec = getdec(q,sc);
+		p = removc(q,sc);
+		(*outdit)(p,1);
+	} while(++ct < dout);
+	release(dec);
+	rewind(strptr);
+	while(sfeof(strptr) == 0)
+		OUTC(sgetc(strptr));
+	Bprint(&bout,"\n");
+}
+
+Blk*
+getdec(Blk *p, int sc)
+{
+	int cc;
+	Blk *q, *t, *s;
+
+	rewind(p);
+	if(length(p)*2 < sc) {
+		q = copy(p,length(p));
+		return(q);
+	}
+	q = salloc(length(p));
+	while(sc >= 1) {
+		sputc(q,sgetc(p));
+		sc -= 2;
+	}
+	if(sc != 0) {
+		t = mult(q,tenptr);
+		s = salloc(cc = length(q));
+		release(q);
+		rewind(t);
+		while(cc-- > 0)
+			sputc(s,sgetc(t));
+		sputc(s,0);
+		release(t);
+		t = div(s,tenptr);
+		release(s);
+		release(rem);
+		return(t);
+	}
+	return(q);
+}
+
+void
+tenot(Blk *p, int sc)
+{
+	int c, f;
+
+	fsfile(p);
+	f=0;
+	while((sfbeg(p) == 0) && ((p->rd-p->beg-1)*2 >= sc)) {
+		c = sbackc(p);
+		if((c<10) && (f == 1))
+			Bprint(&bout,"0%d",c);
+		else
+			Bprint(&bout,"%d",c);
+		f=1;
+		TEST2;
+	}
+	if(sc == 0) {
+		Bprint(&bout,"\n");
+		release(p);
+		return;
+	}
+	if((p->rd-p->beg)*2 > sc) {
+		c = sbackc(p);
+		Bprint(&bout,"%d.",c/10);
+		TEST2;
+		OUTC(c%10 +'0');
+		sc--;
+	} else {
+		OUTC('.');
+	}
+	while(sc>(p->rd-p->beg)*2) {
+		OUTC('0');
+		sc--;
+	}
+	while(sc > 1) {
+		c = sbackc(p);
+		if(c<10)
+			Bprint(&bout,"0%d",c);
+		else
+			Bprint(&bout,"%d",c);
+		sc -= 2;
+		TEST2;
+	}
+	if(sc == 1) {
+		OUTC(sbackc(p)/10 +'0');
+	}
+	Bprint(&bout,"\n");
+	release(p);
+}
+
+void
+oneot(Blk *p, int sc, char ch)
+{
+	Blk *q;
+
+	q = removc(p,sc);
+	create(strptr);
+	sputc(strptr,-1);
+	while(length(q)>0) {
+		p = add(strptr,q);
+		release(q);
+		q = p;
+		OUTC(ch);
+	}
+	release(q);
+	Bprint(&bout,"\n");
+}
+
+void
+hexot(Blk *p, int flg)
+{
+	int c;
+
+	USED(flg);
+	rewind(p);
+	if(sfeof(p) != 0) {
+		sputc(strptr,'0');
+		release(p);
+		return;
+	}
+	c = sgetc(p);
+	release(p);
+	if(c >= 16) {
+		Bprint(&bout,"hex digit > 16");
+		return;
+	}
+	sputc(strptr,c<10?c+'0':c-10+'a');
+}
+
+void
+bigot(Blk *p, int flg)
+{
+	Blk *t, *q;
+	int neg, l;
+
+	if(flg == 1) {
+		t = salloc(0);
+		l = 0;
+	} else {
+		t = strptr;
+		l = length(strptr)+fw-1;
+	}
+	neg=0;
+	if(length(p) != 0) {
+		fsfile(p);
+		if(sbackc(p)<0) {
+			neg=1;
+			chsign(p);
+		}
+		while(length(p) != 0) {
+			q = div(p,tenptr);
+			release(p);
+			p = q;
+			rewind(rem);
+			sputc(t,sfeof(rem)?'0':sgetc(rem)+'0');
+			release(rem);
+		}
+	}
+	release(p);
+	if(flg == 1) {
+		l = fw1-length(t);
+		if(neg != 0) {
+			l--;
+			sputc(strptr,'-');
+		}
+		fsfile(t);
+		while(l-- > 0)
+			sputc(strptr,'0');
+		while(sfbeg(t) == 0)
+			sputc(strptr,sbackc(t));
+		release(t);
+	} else {
+		l -= length(strptr);
+		while(l-- > 0)
+			sputc(strptr,'0');
+		if(neg != 0) {
+			sclobber(strptr);
+			sputc(strptr,'-');
+		}
+	}
+	sputc(strptr,' ');
+}
+
+Blk*
+add(Blk *a1, Blk *a2)
+{
+	Blk *p;
+	int carry, n, size, c, n1, n2;
+
+	size = length(a1)>length(a2)?length(a1):length(a2);
+	p = salloc(size);
+	rewind(a1);
+	rewind(a2);
+	carry=0;
+	while(--size >= 0) {
+		n1 = sfeof(a1)?0:sgetc(a1);
+		n2 = sfeof(a2)?0:sgetc(a2);
+		n = n1 + n2 + carry;
+		if(n>=100) {
+			carry=1;
+			n -= 100;
+		} else
+		if(n<0) {
+			carry = -1;
+			n += 100;
+		} else
+			carry = 0;
+		sputc(p,n);
+	}
+	if(carry != 0)
+		sputc(p,carry);
+	fsfile(p);
+	if(sfbeg(p) == 0) {
+		c = 0;
+		while(sfbeg(p) == 0 && (c = sbackc(p)) == 0)
+			;
+		if(c != 0)
+			salterc(p,c);
+		truncate(p);
+	}
+	fsfile(p);
+	if(sfbeg(p) == 0 && sbackc(p) == -1) {
+		while((c = sbackc(p)) == 99) {
+			if(c == -1)
+				break;
+		}
+		skipc(p);
+		salterc(p,-1);
+		truncate(p);
+	}
+	return(p);
+}
+
+int
+eqk(void)
+{
+	Blk *p, *q;
+	int skp, skq;
+
+	p = pop();
+	EMPTYS;
+	q = pop();
+	EMPTYSR(p);
+	skp = sunputc(p);
+	skq = sunputc(q);
+	if(skp == skq) {
+		arg1=p;
+		arg2=q;
+		savk = skp;
+		return(0);
+	}
+	if(skp < skq) {
+		savk = skq;
+		p = add0(p,skq-skp);
+	} else {
+		savk = skp;
+		q = add0(q,skp-skq);
+	}
+	arg1=p;
+	arg2=q;
+	return(0);
+}
+
+Blk*
+removc(Blk *p, int n)
+{
+	Blk *q, *r;
+
+	rewind(p);
+	while(n>1) {
+		skipc(p);
+		n -= 2;
+	}
+	q = salloc(2);
+	while(sfeof(p) == 0)
+		sputc(q,sgetc(p));
+	if(n == 1) {
+		r = div(q,tenptr);
+		release(q);
+		release(rem);
+		q = r;
+	}
+	release(p);
+	return(q);
+}
+
+Blk*
+scalint(Blk *p)
+{
+	int n;
+
+	n = sunputc(p);
+	p = removc(p,n);
+	return(p);
+}
+
+Blk*
+scale(Blk *p, int n)
+{
+	Blk *q, *s, *t;
+
+	t = add0(p,n);
+	q = salloc(1);
+	sputc(q,n);
+	s = dcexp(inbas,q);
+	release(q);
+	q = div(t,s);
+	release(t);
+	release(s);
+	release(rem);
+	sputc(q,n);
+	return(q);
+}
+
+int
+subt(void)
+{
+	arg1=pop();
+	EMPTYS;
+	savk = sunputc(arg1);
+	chsign(arg1);
+	sputc(arg1,savk);
+	pushp(arg1);
+	if(eqk() != 0)
+		return(1);
+	binop('+');
+	return(0);
+}
+
+int
+command(void)
+{
+	char line[100], *sl;
+	int pid, p, c;
+
+	switch(c = readc()) {
+	case '<':
+		return(cond(NL));
+	case '>':
+		return(cond(NG));
+	case '=':
+		return(cond(NE));
+	default:
+		sl = line;
+		*sl++ = c;
+		while((c = readc()) != '\n')
+			*sl++ = c;
+		*sl = 0;
+		if((pid = fork()) == 0) {
+			execl("/bin/rc","rc","-c",line,0);
+			exits("shell");
+		}
+		for(;;) {
+			if((p = waitpid()) < 0)
+				break;
+			if(p== pid)
+				break;
+		}
+		Bprint(&bout,"!\n");
+		return(0);
+	}
+}
+
+int
+cond(char c)
+{
+	Blk *p;
+	int cc;
+
+	if(subt() != 0)
+		return(1);
+	p = pop();
+	sclobber(p);
+	if(length(p) == 0) {
+		release(p);
+		if(c == '<' || c == '>' || c == NE) {
+			getstk();
+			return(0);
+		}
+		load();
+		return(1);
+	}
+	if(c == '='){
+		release(p);
+		getstk();
+		return(0);
+	}
+	if(c == NE) {
+		release(p);
+		load();
+		return(1);
+	}
+	fsfile(p);
+	cc = sbackc(p);
+	release(p);
+	if((cc<0 && (c == '<' || c == NG)) ||
+	   (cc >0) && (c == '>' || c == NL)) {
+		getstk();
+		return(0);
+	}
+	load();
+	return(1);
+}
+
+void
+load(void)
+{
+	int c;
+	Blk *p, *q, *t, *s;
+
+	c = getstk() & 0377;
+	sptr = stable[c];
+	if(sptr != 0) {
+		p = sptr->val;
+		if(c >= ARRAYST) {
+			q = salloc(length(p));
+			rewind(p);
+			while(sfeof(p) == 0) {
+				s = dcgetwd(p);
+				if(s == 0) {
+					putwd(q, (Blk*)0);
+				} else {
+					t = copy(s,length(s));
+					putwd(q,t);
+				}
+			}
+			pushp(q);
+		} else {
+			q = copy(p,length(p));
+			pushp(q);
+		}
+	} else {
+		q = salloc(1);
+		if(c <= LASTFUN) {
+			Bprint(&bout,"function %c undefined\n",c+'a'-1);
+			sputc(q,'c');
+			sputc(q,'0');
+			sputc(q,' ');
+			sputc(q,'1');
+			sputc(q,'Q');
+		}
+		else
+			sputc(q,0);
+		pushp(q);
+	}
+}
+
+int
+log2(long n)
+{
+	int i;
+
+	if(n == 0)
+		return(0);
+	i=31;
+	if(n<0)
+		return(i);
+	while((n= n<<1) >0)
+		i--;
+	return i-1;
+}
+
+Blk*
+salloc(int size)
+{
+	Blk *hdr;
+	char *ptr;
+
+	all++;
+	lall++;
+	if(all - rel > active)
+		active = all - rel;
+	nbytes += size;
+	lbytes += size;
+	if(nbytes >maxsize)
+		maxsize = nbytes;
+	if(size > longest)
+		longest = size;
+	ptr = malloc((unsigned)size);
+	if(ptr == 0){
+		garbage("salloc");
+		if((ptr = malloc((unsigned)size)) == 0)
+			ospace("salloc");
+	}
+	if((hdr = hfree) == 0)
+		hdr = morehd();
+	hfree = (Blk *)hdr->rd;
+	hdr->rd = hdr->wt = hdr->beg = ptr;
+	hdr->last = ptr+size;
+	return(hdr);
+}
+
+Blk*
+morehd(void)
+{
+	Blk *h, *kk;
+
+	headmor++;
+	nbytes += HEADSZ;
+	hfree = h = (Blk *)malloc(HEADSZ);
+	if(hfree == 0) {
+		garbage("morehd");
+		if((hfree = h = (Blk*)malloc(HEADSZ)) == 0)
+			ospace("headers");
+	}
+	kk = h;
+	while(h<hfree+(HEADSZ/BLK))
+		(h++)->rd = (char*)++kk;
+	(h-1)->rd=0;
+	return(hfree);
+}
+
+Blk*
+copy(Blk *hptr, int size)
+{
+	Blk *hdr;
+	unsigned sz;
+	char *ptr;
+
+	all++;
+	lall++;
+	lcopy++;
+	nbytes += size;
+	lbytes += size;
+	if(size > longest)
+		longest = size;
+	if(size > maxsize)
+		maxsize = size;
+	sz = length(hptr);
+	ptr = nalloc(hptr->beg, size);
+	if(ptr == 0) {
+		garbage("copy");
+		if((ptr = nalloc(hptr->beg, size)) == 0) {
+			Bprint(&bout,"copy size %d\n",size);
+			ospace("copy");
+		}
+	}
+	if((hdr = hfree) == 0)
+		hdr = morehd();
+	hfree = (Blk *)hdr->rd;
+	hdr->rd = hdr->beg = ptr;
+	hdr->last = ptr+size;
+	hdr->wt = ptr+sz;
+	ptr = hdr->wt;
+	while(ptr<hdr->last)
+		*ptr++ = '\0';
+	return(hdr);
+}
+
+void
+sdump(char *s1, Blk *hptr)
+{
+	char *p;
+
+	Bprint(&bout,"%s %lx rd %lx wt %lx beg %lx last %lx\n",
+		s1,hptr,hptr->rd,hptr->wt,hptr->beg,hptr->last);
+	p = hptr->beg;
+	while(p < hptr->wt)
+		Bprint(&bout,"%d ",*p++);
+	Bprint(&bout,"\n");
+}
+
+void
+seekc(Blk *hptr, int n)
+{
+	char *nn,*p;
+
+	nn = hptr->beg+n;
+	if(nn > hptr->last) {
+		nbytes += nn - hptr->last;
+		if(nbytes > maxsize)
+			maxsize = nbytes;
+		lbytes += nn - hptr->last;
+		if(n > longest)
+			longest = n;
+/*		free(hptr->beg); *//**/
+		p = realloc(hptr->beg, n);
+		if(p == 0) {
+/*			hptr->beg = realloc(hptr->beg, hptr->last-hptr->beg);
+**			garbage("seekc");
+**			if((p = realloc(hptr->beg, n)) == 0)
+*/				ospace("seekc");
+		}
+		hptr->beg = p;
+		hptr->wt = hptr->last = hptr->rd = p+n;
+		return;
+	}
+	hptr->rd = nn;
+	if(nn>hptr->wt)
+		hptr->wt = nn;
+}
+
+void
+salterwd(Blk *ahptr, Blk *n)
+{
+	Wblk *hptr;
+
+	hptr = (Wblk*)ahptr;
+	if(hptr->rdw == hptr->lastw)
+		more(ahptr);
+	*hptr->rdw++ = n;
+	if(hptr->rdw > hptr->wtw)
+		hptr->wtw = hptr->rdw;
+}
+
+void
+more(Blk *hptr)
+{
+	unsigned size;
+	char *p;
+
+	if((size=(hptr->last-hptr->beg)*2) == 0)
+		size=2;
+	nbytes += size/2;
+	if(nbytes > maxsize)
+		maxsize = nbytes;
+	if(size > longest)
+		longest = size;
+	lbytes += size/2;
+	lmore++;
+/*	free(hptr->beg);*//**/
+	p = realloc(hptr->beg, size);
+
+	if(p == 0) {
+/*		hptr->beg = realloc(hptr->beg, (hptr->last-hptr->beg));
+**		garbage("more");
+**		if((p = realloc(hptr->beg,size)) == 0)
+*/			ospace("more");
+	}
+	hptr->rd = p + (hptr->rd - hptr->beg);
+	hptr->wt = p + (hptr->wt - hptr->beg);
+	hptr->beg = p;
+	hptr->last = p+size;
+}
+
+void
+ospace(char *s)
+{
+	Bprint(&bout,"out of space: %s\n",s);
+	Bprint(&bout,"all %ld rel %ld headmor %ld\n",all,rel,headmor);
+	Bprint(&bout,"nbytes %ld\n",nbytes);
+	sdump("stk",*stkptr);
+	abort();
+}
+
+void
+garbage(char *s)
+{
+	USED(s);
+}
+
+void
+release(Blk *p)
+{
+	rel++;
+	lrel++;
+	nbytes -= p->last - p->beg;
+	p->rd = (char*)hfree;
+	hfree = p;
+	free(p->beg);
+}
+
+Blk*
+dcgetwd(Blk *p)
+{
+	Wblk *wp;
+
+	wp = (Wblk*)p;
+	if(wp->rdw == wp->wtw)
+		return(0);
+	return(*wp->rdw++);
+}
+
+void
+putwd(Blk *p, Blk *c)
+{
+	Wblk *wp;
+
+	wp = (Wblk*)p;
+	if(wp->wtw == wp->lastw)
+		more(p);
+	*wp->wtw++ = c;
+}
+
+Blk*
+lookwd(Blk *p)
+{
+	Wblk *wp;
+
+	wp = (Wblk*)p;
+	if(wp->rdw == wp->wtw)
+		return(0);
+	return(*wp->rdw);
+}
+
+char*
+nalloc(char *p, unsigned nbytes)
+{
+	char *q, *r;
+
+	q = r = malloc(nbytes);
+	if(q==0)
+		return(0);
+	while(nbytes--)
+		*q++ = *p++;
+	return(r);
+}
+
+int
+getstk(void)
+{
+	int n;
+	uchar c;
+
+	c = readc();
+	if(c != '<')
+		return c;
+	n = 0;
+	while(1) {
+		c = readc();
+		if(c == '>')
+			break;
+		n = n*10+c-'0';
+	}
+	return n;
+}
blob - /dev/null
blob + 1559914534ef6bcfef9e22908e9f942bf7846171 (mode 644)
--- /dev/null
+++ src/cmd/dd.c
@@ -0,0 +1,660 @@
+#include <u.h>
+#include <libc.h>
+
+#define	BIG	2147483647
+#define	LCASE	(1<<0)
+#define	UCASE	(1<<1)
+#define	SWAB	(1<<2)
+#define NERR	(1<<3)
+#define SYNC	(1<<4)
+int	cflag;
+int	fflag;
+char	*string;
+char	*ifile;
+char	*ofile;
+char	*ibuf;
+char	*obuf;
+vlong	skip;
+vlong	oseekn;
+vlong	iseekn;
+vlong	count;
+long	files	= 1;
+long	ibs	= 512;
+long	obs	= 512;
+long	bs;
+long	cbs;
+long	ibc;
+long	obc;
+long	cbc;
+long	nifr;
+long	nipr;
+long	nofr;
+long	nopr;
+long	ntrunc;
+int dotrunc = 1;
+int	ibf;
+int	obf;
+char	*op;
+int	nspace;
+uchar	etoa[256];
+uchar	atoe[256];
+uchar	atoibm[256];
+
+void	flsh(void);
+int	match(char *s);
+vlong	number(long big);
+void	cnull(int cc);
+void	null(int c);
+void	ascii(int cc);
+void	unblock(int cc);
+void	ebcdic(int cc);
+void	ibm(int cc);
+void	block(int cc);
+void	term(void);
+void	stats(void);
+
+#define	iskey(s)	((key[0] == '-') && (strcmp(key+1, s) == 0))
+
+void
+main(int argc, char *argv[])
+{
+	void (*conv)(int);
+	char *ip;
+	char *key;
+	int a, c;
+
+	conv = null;
+	for(c=1; c<argc; c++) {
+		key = argv[c++];
+		if(c >= argc){
+			fprint(2, "dd: arg %s needs a value\n", key);
+			exits("arg");
+		}
+		string = argv[c];
+		if(iskey("ibs")) {
+			ibs = number(BIG);
+			continue;
+		}
+		if(iskey("obs")) {
+			obs = number(BIG);
+			continue;
+		}
+		if(iskey("cbs")) {
+			cbs = number(BIG);
+			continue;
+		}
+		if(iskey("bs")) {
+			bs = number(BIG);
+			continue;
+		}
+		if(iskey("if")) {
+			ifile = string;
+			continue;
+		}
+		if(iskey("of")) {
+			ofile = string;
+			continue;
+		}
+		if(iskey("trunc")) {
+			dotrunc = number(BIG);
+			continue;
+		}
+		if(iskey("skip")) {
+			skip = number(BIG);
+			continue;
+		}
+		if(iskey("seek") || iskey("oseek")) {
+			oseekn = number(BIG);
+			continue;
+		}
+		if(iskey("iseek")) {
+			iseekn = number(BIG);
+			continue;
+		}
+		if(iskey("count")) {
+			count = number(BIG);
+			continue;
+		}
+		if(iskey("files")) {
+			files = number(BIG);
+			continue;
+		}
+		if(iskey("conv")) {
+		cloop:
+			if(match(","))
+				goto cloop;
+			if(*string == '\0')
+				continue;
+			if(match("ebcdic")) {
+				conv = ebcdic;
+				goto cloop;
+			}
+			if(match("ibm")) {
+				conv = ibm;
+				goto cloop;
+			}
+			if(match("ascii")) {
+				conv = ascii;
+				goto cloop;
+			}
+			if(match("block")) {
+				conv = block;
+				goto cloop;
+			}
+			if(match("unblock")) {
+				conv = unblock;
+				goto cloop;
+			}
+			if(match("lcase")) {
+				cflag |= LCASE;
+				goto cloop;
+			}
+			if(match("ucase")) {
+				cflag |= UCASE;
+				goto cloop;
+			}
+			if(match("swab")) {
+				cflag |= SWAB;
+				goto cloop;
+			}
+			if(match("noerror")) {
+				cflag |= NERR;
+				goto cloop;
+			}
+			if(match("sync")) {
+				cflag |= SYNC;
+				goto cloop;
+			}
+		}
+		fprint(2, "dd: bad arg: %s\n", key);
+		exits("arg");
+	}
+	if(conv == null && cflag&(LCASE|UCASE))
+		conv = cnull;
+	if(ifile)
+		ibf = open(ifile, 0);
+	else
+		ibf = dup(0, -1);
+	if(ibf < 0) {
+		fprint(2, "dd: open %s: %r\n", ifile);
+		exits("open");
+	}
+	if(ofile){
+		if(dotrunc)
+			obf = create(ofile, 1, 0664);
+		else
+			obf = open(ofile, 1);
+		if(obf < 0) {
+			fprint(2, "dd: create %s: %r\n", ofile);
+			exits("create");
+		}
+	}else{
+		obf = dup(1, -1);
+		if(obf < 0) {
+			fprint(2, "dd: can't dup file descriptor: %s: %r\n", ofile);
+			exits("dup");
+		}
+	}
+	if(bs)
+		ibs = obs = bs;
+	if(ibs == obs && conv == null)
+		fflag++;
+	if(ibs == 0 || obs == 0) {
+		fprint(2, "dd: counts: cannot be zero\n");
+		exits("counts");
+	}
+	ibuf = sbrk(ibs);
+	if(fflag)
+		obuf = ibuf;
+	else
+		obuf = sbrk(obs);
+	sbrk(64);	/* For good measure */
+	if(ibuf == (char *)-1 || obuf == (char *)-1) {
+		fprint(2, "dd: not enough memory: %r\n");
+		exits("memory");
+	}
+	ibc = 0;
+	obc = 0;
+	cbc = 0;
+	op = obuf;
+
+/*
+	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
+		signal(SIGINT, term);
+*/
+	seek(obf, obs*oseekn, 1);
+	seek(ibf, ibs*iseekn, 1);
+	while(skip) {
+		read(ibf, ibuf, ibs);
+		skip--;
+	}
+
+	ip = 0;
+loop:
+	if(ibc-- == 0) {
+		ibc = 0;
+		if(count==0 || nifr+nipr!=count) {
+			if(cflag&(NERR|SYNC))
+			for(ip=ibuf+ibs; ip>ibuf;)
+				*--ip = 0;
+			ibc = read(ibf, ibuf, ibs);
+		}
+		if(ibc == -1) {
+			perror("read");
+			if((cflag&NERR) == 0) {
+				flsh();
+				term();
+			}
+			ibc = 0;
+			for(c=0; c<ibs; c++)
+				if(ibuf[c] != 0)
+					ibc = c;
+			stats();
+		}
+		if(ibc == 0 && --files<=0) {
+			flsh();
+			term();
+		}
+		if(ibc != ibs) {
+			nipr++;
+			if(cflag&SYNC)
+				ibc = ibs;
+		} else
+			nifr++;
+		ip = ibuf;
+		c = (ibc>>1) & ~1;
+		if(cflag&SWAB && c)
+		do {
+			a = *ip++;
+			ip[-1] = *ip;
+			*ip++ = a;
+		} while(--c);
+		ip = ibuf;
+		if(fflag) {
+			obc = ibc;
+			flsh();
+			ibc = 0;
+		}
+		goto loop;
+	}
+	c = 0;
+	c |= *ip++;
+	c &= 0377;
+	(*conv)(c);
+	goto loop;
+}
+
+void
+flsh(void)
+{
+	int c;
+
+	if(obc) {
+		c = write(obf, obuf, obc);
+		if(c != obc) {
+			if(c > 0)
+				++nopr;
+			perror("write");
+			term();
+		}
+		if(obc == obs)
+			nofr++;
+		else
+			nopr++;
+		obc = 0;
+	}
+}
+
+int
+match(char *s)
+{
+	char *cs;
+
+	cs = string;
+	while(*cs++ == *s)
+		if(*s++ == '\0')
+			goto true;
+	if(*s != '\0')
+		return 0;
+
+true:
+	cs--;
+	string = cs;
+	return 1;
+}
+
+vlong
+number(long big)
+{
+	char *cs;
+	vlong n;
+
+	cs = string;
+	n = 0;
+	while(*cs >= '0' && *cs <= '9')
+		n = n*10 + *cs++ - '0';
+	for(;;)
+	switch(*cs++) {
+
+	case 'k':
+		n *= 1024;
+		continue;
+
+/*	case 'w':
+		n *= sizeof(int);
+		continue;
+*/
+
+	case 'b':
+		n *= 512;
+		continue;
+
+/*	case '*':*/
+	case 'x':
+		string = cs;
+		n *= number(BIG);
+
+	case '\0':
+		if(n>=big || n<0) {
+			fprint(2, "dd: argument %lld out of range\n", n);
+			exits("range");
+		}
+		return n;
+	}
+	/* never gets here */
+}
+
+void
+cnull(int cc)
+{
+	int c;
+
+	c = cc;
+	if((cflag&UCASE) && c>='a' && c<='z')
+		c += 'A'-'a';
+	if((cflag&LCASE) && c>='A' && c<='Z')
+		c += 'a'-'A';
+	null(c);
+}
+
+void
+null(int c)
+{
+
+	*op = c;
+	op++;
+	if(++obc >= obs) {
+		flsh();
+		op = obuf;
+	}
+}
+
+void
+ascii(int cc)
+{
+	int c;
+
+	c = etoa[cc];
+	if(cbs == 0) {
+		cnull(c);
+		return;
+	}
+	if(c == ' ') {
+		nspace++;
+		goto out;
+	}
+	while(nspace > 0) {
+		null(' ');
+		nspace--;
+	}
+	cnull(c);
+
+out:
+	if(++cbc >= cbs) {
+		null('\n');
+		cbc = 0;
+		nspace = 0;
+	}
+}
+
+void
+unblock(int cc)
+{
+	int c;
+
+	c = cc & 0377;
+	if(cbs == 0) {
+		cnull(c);
+		return;
+	}
+	if(c == ' ') {
+		nspace++;
+		goto out;
+	}
+	while(nspace > 0) {
+		null(' ');
+		nspace--;
+	}
+	cnull(c);
+
+out:
+	if(++cbc >= cbs) {
+		null('\n');
+		cbc = 0;
+		nspace = 0;
+	}
+}
+
+void
+ebcdic(int cc)
+{
+	int c;
+
+	c = cc;
+	if(cflag&UCASE && c>='a' && c<='z')
+		c += 'A'-'a';
+	if(cflag&LCASE && c>='A' && c<='Z')
+		c += 'a'-'A';
+	c = atoe[c];
+	if(cbs == 0) {
+		null(c);
+		return;
+	}
+	if(cc == '\n') {
+		while(cbc < cbs) {
+			null(atoe[' ']);
+			cbc++;
+		}
+		cbc = 0;
+		return;
+	}
+	if(cbc == cbs)
+		ntrunc++;
+	cbc++;
+	if(cbc <= cbs)
+		null(c);
+}
+
+void
+ibm(int cc)
+{
+	int c;
+
+	c = cc;
+	if(cflag&UCASE && c>='a' && c<='z')
+		c += 'A'-'a';
+	if(cflag&LCASE && c>='A' && c<='Z')
+		c += 'a'-'A';
+	c = atoibm[c] & 0377;
+	if(cbs == 0) {
+		null(c);
+		return;
+	}
+	if(cc == '\n') {
+		while(cbc < cbs) {
+			null(atoibm[' ']);
+			cbc++;
+		}
+		cbc = 0;
+		return;
+	}
+	if(cbc == cbs)
+		ntrunc++;
+	cbc++;
+	if(cbc <= cbs)
+		null(c);
+}
+
+void
+block(int cc)
+{
+	int c;
+
+	c = cc;
+	if(cflag&UCASE && c>='a' && c<='z')
+		c += 'A'-'a';
+	if(cflag&LCASE && c>='A' && c<='Z')
+		c += 'a'-'A';
+	c &= 0377;
+	if(cbs == 0) {
+		null(c);
+		return;
+	}
+	if(cc == '\n') {
+		while(cbc < cbs) {
+			null(' ');
+			cbc++;
+		}
+		cbc = 0;
+		return;
+	}
+	if(cbc == cbs)
+		ntrunc++;
+	cbc++;
+	if(cbc <= cbs)
+		null(c);
+}
+
+void
+term(void)
+{
+
+	stats();
+	exits(0);
+}
+
+void
+stats(void)
+{
+
+	fprint(2, "%lud+%lud records in\n", nifr, nipr);
+	fprint(2, "%lud+%lud records out\n", nofr, nopr);
+	if(ntrunc)
+		fprint(2, "%lud truncated records\n", ntrunc);
+}
+
+uchar	etoa[] =
+{
+	0000,0001,0002,0003,0234,0011,0206,0177,
+	0227,0215,0216,0013,0014,0015,0016,0017,
+	0020,0021,0022,0023,0235,0205,0010,0207,
+	0030,0031,0222,0217,0034,0035,0036,0037,
+	0200,0201,0202,0203,0204,0012,0027,0033,
+	0210,0211,0212,0213,0214,0005,0006,0007,
+	0220,0221,0026,0223,0224,0225,0226,0004,
+	0230,0231,0232,0233,0024,0025,0236,0032,
+	0040,0240,0241,0242,0243,0244,0245,0246,
+	0247,0250,0133,0056,0074,0050,0053,0041,
+	0046,0251,0252,0253,0254,0255,0256,0257,
+	0260,0261,0135,0044,0052,0051,0073,0136,
+	0055,0057,0262,0263,0264,0265,0266,0267,
+	0270,0271,0174,0054,0045,0137,0076,0077,
+	0272,0273,0274,0275,0276,0277,0300,0301,
+	0302,0140,0072,0043,0100,0047,0075,0042,
+	0303,0141,0142,0143,0144,0145,0146,0147,
+	0150,0151,0304,0305,0306,0307,0310,0311,
+	0312,0152,0153,0154,0155,0156,0157,0160,
+	0161,0162,0313,0314,0315,0316,0317,0320,
+	0321,0176,0163,0164,0165,0166,0167,0170,
+	0171,0172,0322,0323,0324,0325,0326,0327,
+	0330,0331,0332,0333,0334,0335,0336,0337,
+	0340,0341,0342,0343,0344,0345,0346,0347,
+	0173,0101,0102,0103,0104,0105,0106,0107,
+	0110,0111,0350,0351,0352,0353,0354,0355,
+	0175,0112,0113,0114,0115,0116,0117,0120,
+	0121,0122,0356,0357,0360,0361,0362,0363,
+	0134,0237,0123,0124,0125,0126,0127,0130,
+	0131,0132,0364,0365,0366,0367,0370,0371,
+	0060,0061,0062,0063,0064,0065,0066,0067,
+	0070,0071,0372,0373,0374,0375,0376,0377,
+};
+uchar	atoe[] =
+{
+	0000,0001,0002,0003,0067,0055,0056,0057,
+	0026,0005,0045,0013,0014,0015,0016,0017,
+	0020,0021,0022,0023,0074,0075,0062,0046,
+	0030,0031,0077,0047,0034,0035,0036,0037,
+	0100,0117,0177,0173,0133,0154,0120,0175,
+	0115,0135,0134,0116,0153,0140,0113,0141,
+	0360,0361,0362,0363,0364,0365,0366,0367,
+	0370,0371,0172,0136,0114,0176,0156,0157,
+	0174,0301,0302,0303,0304,0305,0306,0307,
+	0310,0311,0321,0322,0323,0324,0325,0326,
+	0327,0330,0331,0342,0343,0344,0345,0346,
+	0347,0350,0351,0112,0340,0132,0137,0155,
+	0171,0201,0202,0203,0204,0205,0206,0207,
+	0210,0211,0221,0222,0223,0224,0225,0226,
+	0227,0230,0231,0242,0243,0244,0245,0246,
+	0247,0250,0251,0300,0152,0320,0241,0007,
+	0040,0041,0042,0043,0044,0025,0006,0027,
+	0050,0051,0052,0053,0054,0011,0012,0033,
+	0060,0061,0032,0063,0064,0065,0066,0010,
+	0070,0071,0072,0073,0004,0024,0076,0341,
+	0101,0102,0103,0104,0105,0106,0107,0110,
+	0111,0121,0122,0123,0124,0125,0126,0127,
+	0130,0131,0142,0143,0144,0145,0146,0147,
+	0150,0151,0160,0161,0162,0163,0164,0165,
+	0166,0167,0170,0200,0212,0213,0214,0215,
+	0216,0217,0220,0232,0233,0234,0235,0236,
+	0237,0240,0252,0253,0254,0255,0256,0257,
+	0260,0261,0262,0263,0264,0265,0266,0267,
+	0270,0271,0272,0273,0274,0275,0276,0277,
+	0312,0313,0314,0315,0316,0317,0332,0333,
+	0334,0335,0336,0337,0352,0353,0354,0355,
+	0356,0357,0372,0373,0374,0375,0376,0377,
+};
+uchar	atoibm[] =
+{
+	0000,0001,0002,0003,0067,0055,0056,0057,
+	0026,0005,0045,0013,0014,0015,0016,0017,
+	0020,0021,0022,0023,0074,0075,0062,0046,
+	0030,0031,0077,0047,0034,0035,0036,0037,
+	0100,0132,0177,0173,0133,0154,0120,0175,
+	0115,0135,0134,0116,0153,0140,0113,0141,
+	0360,0361,0362,0363,0364,0365,0366,0367,
+	0370,0371,0172,0136,0114,0176,0156,0157,
+	0174,0301,0302,0303,0304,0305,0306,0307,
+	0310,0311,0321,0322,0323,0324,0325,0326,
+	0327,0330,0331,0342,0343,0344,0345,0346,
+	0347,0350,0351,0255,0340,0275,0137,0155,
+	0171,0201,0202,0203,0204,0205,0206,0207,
+	0210,0211,0221,0222,0223,0224,0225,0226,
+	0227,0230,0231,0242,0243,0244,0245,0246,
+	0247,0250,0251,0300,0117,0320,0241,0007,
+	0040,0041,0042,0043,0044,0025,0006,0027,
+	0050,0051,0052,0053,0054,0011,0012,0033,
+	0060,0061,0032,0063,0064,0065,0066,0010,
+	0070,0071,0072,0073,0004,0024,0076,0341,
+	0101,0102,0103,0104,0105,0106,0107,0110,
+	0111,0121,0122,0123,0124,0125,0126,0127,
+	0130,0131,0142,0143,0144,0145,0146,0147,
+	0150,0151,0160,0161,0162,0163,0164,0165,
+	0166,0167,0170,0200,0212,0213,0214,0215,
+	0216,0217,0220,0232,0233,0234,0235,0236,
+	0237,0240,0252,0253,0254,0255,0256,0257,
+	0260,0261,0262,0263,0264,0265,0266,0267,
+	0270,0271,0272,0273,0274,0275,0276,0277,
+	0312,0313,0314,0315,0316,0317,0332,0333,
+	0334,0335,0336,0337,0352,0353,0354,0355,
+	0356,0357,0372,0373,0374,0375,0376,0377,
+};
blob - /dev/null
blob + 914c5a3f5f1e38832b669a1566ca0c3c446bf6bf (mode 644)
--- /dev/null
+++ src/cmd/deroff.c
@@ -0,0 +1,969 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+/*
+ * Deroff command -- strip troff, eqn, and tbl sequences from
+ * a file.  Has three flags argument, -w, to cause output one word per line
+ * rather than in the original format.
+ * -mm (or -ms) causes the corresponding macro's to be interpreted
+ * so that just sentences are output
+ * -ml  also gets rid of lists.
+ * -i causes deroff to ignore .so and .nx commands.
+ * Deroff follows .so and .nx commands, removes contents of macro
+ * definitions, equations (both .EQ ... .EN and $...$),
+ * Tbl command sequences, and Troff backslash vconstructions.
+ * 
+ * All input is through the C macro; the most recently read character is in c.
+ */
+
+/*
+#define	C	((c = Bgetrune(infile)) < 0?\
+			eof():\
+			((c == ldelim) && (filesp == files)?\
+				skeqn():\
+				(c == '\n'?\
+					(linect++,c):\
+						c)))
+
+#define	C1	((c = Bgetrune(infile)) == Beof?\
+			eof():\
+			(c == '\n'?\
+				(linect++,c):\
+				c))
+*/
+
+/* lose those macros! */
+#define	C	fC()
+#define	C1	fC1()
+
+#define	SKIP	while(C != '\n') 
+#define SKIP1	while(C1 != '\n')
+#define SKIP_TO_COM		SKIP;\
+				SKIP;\
+				pc=c;\
+				while(C != '.' || pc != '\n' || C > 'Z')\
+						pc=c
+
+#define YES		1
+#define NO		0
+#define MS		0
+#define MM		1
+#define ONE		1
+#define TWO		2
+
+#define NOCHAR		-2
+#define	EXTENDED	-1		/* All runes above 0x7F */
+#define SPECIAL		0
+#define APOS		1
+#define PUNCT		2
+#define DIGIT		3
+#define LETTER		4
+
+
+int	linect	= 0;
+int	wordflag= NO;
+int	underscoreflag = NO;
+int	msflag	= NO;
+int	iflag	= NO;
+int	mac	= MM;
+int	disp	= 0;
+int	inmacro	= NO;
+int	intable	= NO;
+int	eqnflag	= 0;
+
+#define	MAX_ASCII	0X80
+
+char	chars[MAX_ASCII];	/* SPECIAL, PUNCT, APOS, DIGIT, or LETTER */
+
+Rune	line[30000];
+Rune*	lp;
+
+long	c;
+long	pc;
+int	ldelim	= NOCHAR;
+int	rdelim	= NOCHAR;
+
+
+char**	argv;
+
+char	fname[50];
+Biobuf*	files[15];
+Biobuf**filesp;
+Biobuf*	infile;
+char*	devnull	= "/dev/null";
+Biobuf	*infile;
+Biobuf	bout;
+
+long	skeqn(void);
+Biobuf*	opn(char *p);
+int	eof(void);
+int	charclass(int);
+void	getfname(void);
+void	fatal(char *s, char *p);
+void	usage(void);
+void	work(void);
+void	putmac(Rune *rp, int vconst);
+void	regline(int macline, int vconst);
+void	putwords(void);
+void	comline(void);
+void	macro(void);
+void	eqn(void);
+void	tbl(void);
+void	stbl(void);
+void	sdis(char a1, char a2);
+void	sce(void);
+void	backsl(void);
+char*	copys(char *s);
+void	refer(int c1);
+void	inpic(void);
+
+int
+fC(void)
+{
+	c = Bgetrune(infile);
+	if(c < 0)
+		return eof();
+	if(c == ldelim && filesp == files)
+		return skeqn();
+	if(c == '\n')
+		linect++;
+	return c;
+}
+
+int
+fC1(void)
+{
+	c = Bgetrune(infile);
+	if(c == Beof)
+		return eof();
+	if(c == '\n')
+		linect++;
+	return c;
+}
+
+void
+main(int argc, char *av[])
+{
+	int i;
+	char *f;
+
+	argv = av;
+	Binit(&bout, 1, OWRITE);
+	ARGBEGIN{
+	case 'w':
+		wordflag = YES;
+		break;
+	case '_':
+		wordflag = YES;
+		underscoreflag = YES;
+		break;
+	case 'm':
+		msflag = YES;
+		if(f = ARGF())
+			switch(*f)
+			{
+			case 'm':	mac = MM; break;
+			case 's':	mac = MS; break;
+			case 'l':	disp = 1; break;
+			default:	usage();
+			}
+		else
+			usage();
+		break;
+	case 'i':
+		iflag = YES;
+		break;
+	default:
+		usage();
+	}ARGEND
+	if(*argv)
+		infile = opn(*argv++);
+	else{
+		infile = malloc(sizeof(Biobuf));
+		Binit(infile, 0, OREAD);
+	}
+	files[0] = infile;
+	filesp = &files[0];
+
+	for(i='a'; i<='z' ; ++i)
+		chars[i] = LETTER;
+	for(i='A'; i<='Z'; ++i)
+		chars[i] = LETTER;
+	for(i='0'; i<='9'; ++i)
+		chars[i] = DIGIT;
+	chars['\''] = APOS;
+	chars['&'] = APOS;
+	chars['\b'] = APOS;
+	chars['.'] = PUNCT;
+	chars[','] = PUNCT;
+	chars[';'] = PUNCT;
+	chars['?'] = PUNCT;
+	chars[':'] = PUNCT;
+	work();
+}
+
+long
+skeqn(void)
+{
+	while(C1 != rdelim)
+		if(c == '\\')
+			c = C1;
+		else if(c == '"')
+			while(C1 != '"')
+				if(c == '\\') 
+					C1;
+	if (msflag)
+		eqnflag = 1;
+	return(c = ' ');
+}
+
+Biobuf*
+opn(char *p)
+{
+	Biobuf *fd;
+
+	while ((fd = Bopen(p, OREAD)) == 0) {
+		if(msflag || p == devnull)
+			fatal("Cannot open file %s - quitting\n", p);
+		else {
+			fprint(2, "Deroff: Cannot open file %s - continuing\n", p);
+			p = devnull;
+		}
+	}
+	linect = 0;
+	return(fd);
+}
+
+int
+eof(void)
+{
+	if(Bfildes(infile) != 0)
+		Bterm(infile);
+	if(filesp > files)
+		infile = *--filesp;
+	else
+	if(*argv)
+		infile = opn(*argv++);
+	else
+		exits(0);
+	return(C);
+}
+
+void
+getfname(void)
+{
+	char *p;
+	Rune r;
+	Dir *dir;
+	struct chain
+	{ 
+		struct	chain*	nextp; 
+		char*	datap; 
+	} *q;
+
+	static struct chain *namechain= 0;
+
+	while(C == ' ')
+		;
+	for(p = fname; (r=c) != '\n' && r != ' ' && r != '\t' && r != '\\'; C)
+		p += runetochar(p, &r);
+	*p = '\0';
+	while(c != '\n')
+		C;
+	if(!strcmp(fname, "/sys/lib/tmac/tmac.cs")
+			|| !strcmp(fname, "/sys/lib/tmac/tmac.s")) {
+		fname[0] = '\0';
+		return;
+	}
+	dir = dirstat(fname);
+	if(dir!=nil && ((dir->mode & DMDIR) || dir->type != 'M')) {
+		free(dir);
+		fname[0] = '\0';
+		return;
+	}
+	free(dir);
+	/*
+	 * see if this name has already been used
+	 */
+
+	for(q = namechain; q; q = q->nextp)
+		if( !strcmp(fname, q->datap)) {
+			fname[0] = '\0';
+			return;
+		}
+	q = (struct chain*)malloc(sizeof(struct chain));
+	q->nextp = namechain;
+	q->datap = copys(fname);
+	namechain = q;
+}
+
+void
+usage(void)
+{
+	fprint(2,"usage: deroff [-nw_pi] [-m (m s l)] [file ...] \n");
+	exits("usage");
+}
+
+void
+fatal(char *s, char *p)
+{
+	fprint(2, "deroff: ");
+	fprint(2, s, p);
+	exits(s);
+}
+
+void
+work(void)
+{
+
+	for(;;) {
+		eqnflag = 0;
+		if(C == '.'  ||  c == '\'')
+			comline();
+		else
+			regline(NO, TWO);
+	}
+}
+
+void
+regline(int macline, int vconst)
+{
+	line[0] = c;
+	lp = line;
+	for(;;) {
+		if(c == '\\') {
+			*lp = ' ';
+			backsl();
+			if(c == '%')	/* no blank for hyphenation char */
+				lp--;
+		}
+		if(c == '\n')
+			break;
+		if(intable && c=='T') {
+			*++lp = C;
+			if(c=='{' || c=='}') {
+				lp[-1] = ' ';
+				*lp = C;
+			}
+		} else {
+			if(msflag == 1 && eqnflag == 1) {
+				eqnflag = 0;
+				*++lp = 'x';
+			}
+			*++lp = C;
+		}
+	}
+	*lp = '\0';
+	if(lp != line) {
+		if(wordflag)
+			putwords();
+		else
+		if(macline)
+			putmac(line,vconst);
+		else
+			Bprint(&bout, "%S\n", line);
+	}
+}
+
+void
+putmac(Rune *rp, int vconst)
+{
+	Rune *t;
+	int found;
+	Rune last;
+
+	found = 0;
+	last = 0;
+	while(*rp) {
+		while(*rp == ' ' || *rp == '\t')
+			Bputrune(&bout, *rp++);
+		for(t = rp; *t != ' ' && *t != '\t' && *t != '\0'; t++)
+			;
+		if(*rp == '\"')
+			rp++;
+		if(t > rp+vconst && charclass(*rp) == LETTER
+				&& charclass(rp[1]) == LETTER) {
+			while(rp < t)
+				if(*rp == '\"')
+					rp++;
+				else
+					Bputrune(&bout, *rp++);
+			last = t[-1];
+			found++;
+		} else
+		if(found && charclass(*rp) == PUNCT && rp[1] == '\0')
+			Bputrune(&bout, *rp++);
+		else {
+			last = t[-1];
+			rp = t;
+		}
+	}
+	Bputc(&bout, '\n');
+	if(msflag && charclass(last) == PUNCT)
+		Bprint(&bout, " %C\n", last);
+}
+
+/*
+ * break into words for -w option
+ */
+void
+putwords(void)
+{
+	Rune *p, *p1;
+	int i, nlet;
+
+
+	for(p1 = line;;) {
+		/*
+		 * skip initial specials ampersands and apostrophes
+		 */
+		while((i = charclass(*p1)) != EXTENDED && i < DIGIT)
+			if(*p1++ == '\0')
+				return;
+		nlet = 0;
+		for(p = p1; (i = charclass(*p)) != SPECIAL || (underscoreflag && *p=='_'); p++)
+			if(i == LETTER || (underscoreflag && *p == '_'))
+				nlet++;
+		/*
+		 * MDM definition of word
+		 */
+		if(nlet > 1) {
+			/*
+			 * delete trailing ampersands and apostrophes
+			 */
+			while(*--p == '\'' || *p == '&'
+					   || charclass(*p) == PUNCT)
+				;
+			while(p1 <= p)
+				Bputrune(&bout, *p1++);
+			Bputc(&bout, '\n');
+		} else
+			p1 = p;
+	}
+}
+
+void
+comline(void)
+{
+	long c1, c2;
+
+	while(C==' ' || c=='\t')
+		;
+comx:
+	if((c1=c) == '\n')
+		return;
+	c2 = C;
+	if(c1=='.' && c2!='.')
+		inmacro = NO;
+	if(msflag && c1 == '['){
+		refer(c2);
+		return;
+	}
+	if(c2 == '\n')
+		return;
+	if(c1 == '\\' && c2 == '\"')
+		SKIP;
+	else
+	if (filesp==files && c1=='E' && c2=='Q')
+			eqn();
+	else
+	if(filesp==files && c1=='T' && (c2=='S' || c2=='C' || c2=='&')) {
+		if(msflag)
+			stbl(); 
+		else
+			tbl();
+	}
+	else
+	if(c1=='T' && c2=='E')
+		intable = NO;
+	else if (!inmacro &&
+			((c1 == 'd' && c2 == 'e') ||
+		   	 (c1 == 'i' && c2 == 'g') ||
+		   	 (c1 == 'a' && c2 == 'm')))
+				macro();
+	else
+	if(c1=='s' && c2=='o') {
+		if(iflag)
+			SKIP;
+		else {
+			getfname();
+			if(fname[0]) {
+				if(infile = opn(fname))
+					*++filesp = infile;
+				else infile = *filesp;
+			}
+		}
+	}
+	else
+	if(c1=='n' && c2=='x')
+		if(iflag)
+			SKIP;
+		else {
+			getfname();
+			if(fname[0] == '\0')
+				exits(0);
+			if(Bfildes(infile) != 0)
+				Bterm(infile);
+			infile = *filesp = opn(fname);
+		}
+	else
+	if(c1 == 't' && c2 == 'm')
+		SKIP;
+	else
+	if(c1=='h' && c2=='w')
+		SKIP; 
+	else
+	if(msflag && c1 == 'T' && c2 == 'L') {
+		SKIP_TO_COM;
+		goto comx; 
+	}
+	else
+	if(msflag && c1=='N' && c2 == 'R')
+		SKIP;
+	else
+	if(msflag && c1 == 'A' && (c2 == 'U' || c2 == 'I')){
+		if(mac==MM)SKIP;
+		else {
+			SKIP_TO_COM;
+			goto comx; 
+		}
+	} else
+	if(msflag && c1=='F' && c2=='S') {
+		SKIP_TO_COM;
+		goto comx; 
+	}
+	else
+	if(msflag && (c1=='S' || c1=='N') && c2=='H') {
+		SKIP_TO_COM;
+		goto comx; 
+	} else
+	if(c1 == 'U' && c2 == 'X') {
+		if(wordflag)
+			Bprint(&bout, "UNIX\n");
+		else
+			Bprint(&bout, "UNIX ");
+	} else
+	if(msflag && c1=='O' && c2=='K') {
+		SKIP_TO_COM;
+		goto comx; 
+	} else
+	if(msflag && c1=='N' && c2=='D')
+		SKIP;
+	else
+	if(msflag && mac==MM && c1=='H' && (c2==' '||c2=='U'))
+		SKIP;
+	else
+	if(msflag && mac==MM && c2=='L') {
+		if(disp || c1=='R')
+			sdis('L', 'E');
+		else {
+			SKIP;
+			Bprint(&bout, " .");
+		}
+	} else
+	if(!msflag && c1=='P' && c2=='S') {
+		inpic();
+	} else
+	if(msflag && (c1=='D' || c1=='N' || c1=='K'|| c1=='P') && c2=='S') { 
+		sdis(c1, 'E'); 
+	} else
+	if(msflag && (c1 == 'K' && c2 == 'F')) { 
+		sdis(c1,'E'); 
+	} else
+	if(msflag && c1=='n' && c2=='f')
+		sdis('f','i');
+	else
+	if(msflag && c1=='c' && c2=='e')
+		sce();
+	else {
+		if(c1=='.' && c2=='.') {
+			if(msflag) {
+				SKIP;
+				return;
+			}
+			while(C == '.')
+				;
+		}
+		inmacro++;
+		if(c1 <= 'Z' && msflag)
+			regline(YES,ONE);
+		else {
+			if(wordflag)
+				C;
+			regline(YES,TWO);
+		}
+		inmacro--;
+	}
+}
+
+void
+macro(void)
+{
+	if(msflag) {
+		do { 
+			SKIP1; 
+		} while(C1 != '.' || C1 != '.' || C1 == '.');
+		if(c != '\n')
+			SKIP;
+		return;
+	}
+	SKIP;
+	inmacro = YES;
+}
+
+void
+sdis(char a1, char a2)
+{
+	int c1, c2;
+	int eqnf;
+	int lct;
+
+	if(a1 == 'P'){
+		while(C1 == ' ')
+			;
+		if(c == '<') {
+			SKIP1;
+			return;
+		}
+	}
+	lct = 0;
+	eqnf = 1;
+	if(c != '\n')
+		SKIP1;
+	for(;;) {
+		while(C1 != '.')
+			if(c == '\n')
+				continue;
+			else
+				SKIP1;
+		if((c1=C1) == '\n')
+			continue;
+		if((c2=C1) == '\n') {
+			if(a1 == 'f' && (c1 == 'P' || c1 == 'H'))
+				return;
+			continue;
+		}
+		if(c1==a1 && c2 == a2) {
+			SKIP1;
+			if(lct != 0){
+				lct--;
+				continue;
+			}
+			if(eqnf)
+				Bprint(&bout, " .");
+			Bputc(&bout, '\n');
+			return;
+		} else
+		if(a1 == 'L' && c2 == 'L') {
+			lct++;
+			SKIP1;
+		} else
+		if(a1 == 'D' && c1 == 'E' && c2 == 'Q') {
+			eqn(); 
+			eqnf = 0;
+		} else
+		if(a1 == 'f') {
+			if((mac == MS && c2 == 'P') ||
+				(mac == MM && c1 == 'H' && c2 == 'U')){
+				SKIP1;
+				return;
+			}
+			SKIP1;
+		}
+		else
+			SKIP1;
+	}
+}
+
+void
+tbl(void)
+{
+	while(C != '.')
+		;
+	SKIP;
+	intable = YES;
+}
+
+void
+stbl(void)
+{
+	while(C != '.')
+		;
+	SKIP_TO_COM;
+	if(c != 'T' || C != 'E') {
+		SKIP;
+		pc = c;
+		while(C != '.' || pc != '\n' || C != 'T' || C != 'E')
+			pc = c;
+	}
+}
+
+void
+eqn(void)
+{
+	long c1, c2;
+	int dflg;
+	char last;
+
+	last = 0;
+	dflg = 1;
+	SKIP;
+
+	for(;;) {
+		if(C1 == '.'  || c == '\'') {
+			while(C1==' ' || c=='\t')
+				;
+			if(c=='E' && C1=='N') {
+				SKIP;
+				if(msflag && dflg) {
+					Bputc(&bout, 'x');
+					Bputc(&bout, ' ');
+					if(last) {
+						Bputc(&bout, last); 
+						Bputc(&bout, '\n'); 
+					}
+				}
+				return;
+			}
+		} else
+		if(c == 'd') {
+			if(C1=='e' && C1=='l')
+				if(C1=='i' && C1=='m') {
+					while(C1 == ' ')
+						;
+					if((c1=c)=='\n' || (c2=C1)=='\n' ||
+					  (c1=='o' && c2=='f' && C1=='f')) {
+						ldelim = NOCHAR;
+						rdelim = NOCHAR;
+					} else {
+						ldelim = c1;
+						rdelim = c2;
+					}
+				}
+			dflg = 0;
+		}
+		if(c != '\n')
+			while(C1 != '\n') { 
+				if(chars[c] == PUNCT)
+					last = c;
+				else
+				if(c != ' ')
+					last = 0;
+			}
+	}
+}
+
+/*
+ * skip over a complete backslash vconstruction
+ */
+void
+backsl(void)
+{
+	int bdelim;
+
+sw:  
+	switch(C1)
+	{
+	case '"':
+		SKIP1;
+		return;
+
+	case 's':
+		if(C1 == '\\')
+			backsl();
+		else {
+			while(C1>='0' && c<='9')
+				;
+			Bungetrune(infile);
+			c = '0';
+		}
+		lp--;
+		return;
+
+	case 'f':
+	case 'n':
+	case '*':
+		if(C1 != '(')
+			return;
+
+	case '(':
+		if(msflag) {
+			if(C == 'e') {
+				if(C1 == 'm') {
+					*lp = '-';
+					return;
+				}
+			} else
+			if(c != '\n')
+				C1;
+			return;
+		}
+		if(C1 != '\n')
+			C1;
+		return;
+
+	case '$':
+		C1;	/* discard argument number */
+		return;
+
+	case 'b':
+	case 'x':
+	case 'v':
+	case 'h':
+	case 'w':
+	case 'o':
+	case 'l':
+	case 'L':
+		if((bdelim=C1) == '\n')
+			return;
+		while(C1!='\n' && c!=bdelim)
+			if(c == '\\')
+				backsl();
+		return;
+
+	case '\\':
+		if(inmacro)
+			goto sw;
+	default:
+		return;
+	}
+}
+
+char*
+copys(char *s)
+{
+	char *t, *t0;
+
+	if((t0 = t = malloc((strlen(s)+1))) == 0)
+		fatal("Cannot allocate memory", (char*)0);
+	while(*t++ = *s++)
+		;
+	return(t0);
+}
+
+void
+sce(void)
+{
+	int n = 1;
+
+	while (C != L'\n' && !(L'0' <= c && c <= L'9'))
+		;
+	if (c != L'\n') {
+		for (n = c-L'0';'0' <= C && c <= L'9';)
+			n = n*10 + c-L'0';
+	}
+	while(n) {
+		if(C == '.') {
+			if(C == 'c') {
+				if(C == 'e') {
+					while(C == ' ')
+						;
+					if(c == '0') {
+						SKIP;
+						break;
+					} else
+						SKIP;
+				} else
+					SKIP;
+			} else
+			if(c == 'P' || C == 'P') {
+				if(c != '\n')
+					SKIP;
+				break;
+			} else
+				if(c != '\n')
+					SKIP;
+		} else {
+			SKIP;
+			n--;
+		}
+	}
+}
+
+void
+refer(int c1)
+{
+	int c2;
+
+	if(c1 != '\n')
+		SKIP;
+	c2 = 0;
+	for(;;) {
+		if(C != '.')
+			SKIP;
+		else {
+			if(C != ']')
+				SKIP;
+			else {
+				while(C != '\n')
+					c2 = c;
+				if(charclass(c2) == PUNCT)
+					Bprint(&bout, " %C",c2);
+				return;
+			}
+		}
+	}
+}
+
+void
+inpic(void)
+{
+	int c1;
+	Rune *p1;
+
+/*	SKIP1;*/
+	while(C1 != '\n')
+		if(c == '<'){
+			SKIP1;
+			return;
+		}
+	p1 = line;
+	c = '\n';
+	for(;;) {
+		c1 = c;
+		if(C1 == '.' && c1 == '\n') {
+			if(C1 != 'P' || C1 != 'E') {
+				if(c != '\n'){
+					SKIP1;
+					c = '\n';
+				}
+				continue;
+			}
+			SKIP1;
+			return;
+		} else
+		if(c == '\"') {
+			while(C1 != '\"') {
+				if(c == '\\') {
+					if(C1 == '\"')
+						continue;
+					Bungetrune(infile);
+					backsl();
+				} else
+					*p1++ = c;
+			}
+			*p1++ = ' ';
+		} else
+		if(c == '\n' && p1 != line) {
+			*p1 = '\0';
+			if(wordflag)
+				putwords();
+			else
+				Bprint(&bout, "%S\n\n", line);
+			p1 = line;
+		}
+	}
+}
+
+int
+charclass(int c)
+{
+	if(c < MAX_ASCII)
+		return chars[c];
+	switch(c){
+	case 0x2013: case 0x2014:	/* en dash, em dash */
+		return SPECIAL;
+	}
+	return EXTENDED;
+}
blob - /dev/null
blob + 3a96c598c472f66c91e724570cdd2d97c2577560 (mode 644)
--- /dev/null
+++ src/cmd/du.C
@@ -0,0 +1,194 @@
+#include <u.h>
+#include <libc.h>
+
+extern	vlong	du(char*, Dir*);
+extern	vlong	k(vlong);
+extern	void	err(char*);
+extern	int	warn(char*);
+extern	int	seen(Dir*);
+
+int	aflag;
+int	fflag;
+int	nflag;
+int	sflag;
+int	tflag;
+int	uflag;
+int	qflag;
+char	*fmt = "%llud\t%s\n";
+vlong	blocksize = 1024LL;
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	char *s, *ss;
+
+	ARGBEGIN {
+	case 'a':	/* all files */
+		aflag = 1;
+		break;
+	case 's':	/* only top level */
+		sflag = 1;
+		break;
+	case 'f':	/* ignore errors */
+		fflag = 1;
+		break;
+	case 'n':	/* all files, number of bytes */
+		aflag = 1;
+		nflag = 1;
+		break;
+	case 't':	/* return modified/accessed time */
+		tflag = 1;
+		break;
+	case 'u':	/* accessed time */
+		uflag = 1;
+		break;
+	case 'q':	/* qid */
+		fmt = "%.16llux\t%s\n";
+		qflag = 1;
+		break;
+	case 'b':	/* block size */
+		s = ARGF();
+		if(s) {
+			blocksize = strtoul(s, &ss, 0);
+			if(s == ss)
+				blocksize = 1;
+			if(*ss == 'k')
+				blocksize *= 1024;
+		}
+		break;
+	} ARGEND
+	if(argc==0)
+		print(fmt, du(".", dirstat(".")), ".");
+	else
+		for(i=0; i<argc; i++)
+			print(fmt, du(argv[i], dirstat(argv[i])), argv[i]);
+	exits(0);
+}
+
+vlong
+du(char *name, Dir *dir)
+{
+	int fd, i, n;
+	Dir *buf, *d;
+	char file[256];
+	vlong nk, t;
+
+	if(dir == nil)
+		return warn(name);
+
+	fd = open(name, OREAD);
+	if(fd < 0)
+		return warn(name);
+
+	if((dir->qid.type&QTDIR) == 0)
+		nk = k(dir->length);
+	else{
+		nk = 0;
+		while((n=dirread(fd, &buf)) > 0) {
+			d = buf;
+			for(i=0; i<n; i++, d++) {
+				if((d->qid.type&QTDIR) == 0) {
+					t = k(d->length);
+					nk += t;
+					if(aflag) {
+						sprint(file, "%s/%s", name, d->name);
+						if(tflag) {
+							t = d->mtime;
+							if(uflag)
+								t = d->atime;
+						}
+						if(qflag)
+							t = d->qid.path;
+						print(fmt, t, file);
+					}
+					continue;
+				}
+				if(strcmp(d->name, ".") == 0 ||
+				   strcmp(d->name, "..") == 0 ||
+				   seen(d))
+					continue;
+				sprint(file, "%s/%s", name, d->name);
+				t = du(file, d);
+				nk += t;
+				if(tflag) {
+					t = d->mtime;
+					if(uflag)
+						t = d->atime;
+				}
+				if(qflag)
+					t = d->qid.path;
+				if(!sflag)
+					print(fmt, t, file);
+			}
+			free(buf);
+		}
+		if(n < 0)
+			warn(name);
+	}
+	close(fd);
+	if(tflag) {
+		if(uflag)
+			return dir->atime;
+		return dir->mtime;
+	}
+	if(qflag)
+		return dir->qid.path;
+	return nk;
+}
+
+#define	NCACHE	128	/* must be power of two */
+typedef	struct	Cache	Cache;
+struct	Cache
+{
+	Dir*	cache;
+	int	n;
+	int	max;
+} cache[NCACHE];
+
+int
+seen(Dir *dir)
+{
+	Dir *dp;
+	int i;
+	Cache *c;
+
+	c = &cache[dir->qid.path&(NCACHE-1)];
+	dp = c->cache;
+	for(i=0; i<c->n; i++, dp++)
+		if(dir->qid.path == dp->qid.path &&
+		   dir->type == dp->type &&
+		   dir->dev == dp->dev)
+			return 1;
+	if(c->n == c->max){
+		c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir));
+		if(cache == 0)
+			err("malloc failure");
+	}
+	c->cache[c->n++] = *dir;
+	return 0;
+}
+
+void
+err(char *s)
+{
+	fprint(2, "du: %s: %r\n", s);
+	exits(s);
+}
+
+int
+warn(char *s)
+{
+	if(fflag == 0)
+		fprint(2, "du: %s: %r\n", s);
+	return 0;
+}
+
+vlong
+k(vlong n)
+{
+	if(nflag)
+		return n;
+	n = (n+blocksize-1)/blocksize;
+	return n*blocksize/1024LL;
+}
blob - /dev/null
blob + 25b69692a5ba6285467a46ae10441f1d5eed4d24 (mode 644)
--- /dev/null
+++ src/cmd/echo.c
@@ -0,0 +1,38 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+	int nflag;
+	int i, len;
+	char *buf, *p;
+
+	nflag = 0;
+	if(argc > 1 && strcmp(argv[1], "-n") == 0)
+		nflag = 1;
+
+	len = 1;
+	for(i = 1+nflag; i < argc; i++)
+		len += strlen(argv[i])+1;
+
+	buf = malloc(len);
+	if(buf == 0)
+		exits("no memory");
+
+	p = buf;
+	for(i = 1+nflag; i < argc; i++){
+		strcpy(p, argv[i]);
+		p += strlen(p);
+		if(i < argc-1)
+			*p++ = ' ';
+	}
+		
+	if(!nflag)
+		*p++ = '\n';
+
+	if(write(1, buf, p-buf) < 0)
+		fprint(2, "echo: write error: %r\n");
+
+	exits((char *)0);
+}
blob - /dev/null
blob + 42fa3e076aaa84fecd7be0eb5d85221bec3f51a0 (mode 644)
--- /dev/null
+++ src/cmd/ed.c
@@ -0,0 +1,1608 @@
+/*
+ * Editor
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+
+enum
+{
+	FNSIZE	= 128,		/* file name */
+	LBSIZE	= 4096,		/* max line size */
+	BLKSIZE	= 4096,		/* block size in temp file */
+	NBLK	= 8191,		/* max size of temp file */
+	ESIZE	= 256,		/* max size of reg exp */
+	GBSIZE	= 256,		/* max size of global command */
+	MAXSUB	= 9,		/* max number of sub reg exp */
+	ESCFLG	= 0xFFFF,	/* escape Rune - user defined code */
+	EOF	= -1,
+};
+
+void	(*oldhup)(int);
+void	(*oldquit)(int);
+int*	addr1;
+int*	addr2;
+int	anymarks;
+int	col;
+long	count;
+int*	dol;
+int*	dot;
+int	fchange;
+char	file[FNSIZE];
+Rune	genbuf[LBSIZE];
+int	given;
+Rune*	globp;
+int	iblock;
+int	ichanged;
+int	io;
+Biobuf	iobuf;
+int	lastc;
+char	line[70];
+Rune*	linebp;
+Rune	linebuf[LBSIZE];
+int	listf;
+int	listn;
+Rune*	loc1;
+Rune*	loc2;
+int	names[26];
+int	nleft;
+int	oblock;
+int	oflag;
+Reprog	*pattern;
+int	peekc;
+int	pflag;
+int	rescuing;
+Rune	rhsbuf[LBSIZE/2];
+char	savedfile[FNSIZE];
+jmp_buf	savej;
+int	subnewa;
+int	subolda;
+Resub	subexp[MAXSUB];
+char*	tfname;
+int	tline;
+int	waiting;
+int	wrapp;
+int*	zero;
+
+char	Q[]	= "";
+char	T[]	= "TMP";
+char	WRERR[]	= "WRITE ERROR";
+int	bpagesize = 20;
+char	hex[]	= "0123456789abcdef";
+char*	linp	= line;
+ulong	nlall = 128;
+int	tfile	= -1;
+int	vflag	= 1;
+
+void	add(int);
+int*	address(void);
+int	append(int(*)(void), int*);
+void	browse(void);
+void	callunix(void);
+void	commands(void);
+void	compile(int);
+int	compsub(void);
+void	dosub(void);
+void	error(char*);
+int	match(int*);
+void	exfile(int);
+void	filename(int);
+Rune*	getblock(int, int);
+int	getchr(void);
+int	getcopy(void);
+int	getfile(void);
+Rune*	getline(int);
+int	getnum(void);
+int	getsub(void);
+int	gettty(void);
+void	global(int);
+void	init(void);
+void	join(void);
+void	move(int);
+void	newline(void);
+void	nonzero(void);
+void	notifyf(void*, char*);
+Rune*	place(Rune*, Rune*, Rune*);
+void	printcom(void);
+void	putchr(int);
+void	putd(void);
+void	putfile(void);
+int	putline(void);
+void	putshst(Rune*);
+void	putst(char*);
+void	quit(void);
+void	rdelete(int*, int*);
+void	regerror(char *);
+void	reverse(int*, int*);
+void	setnoaddr(void);
+void	setwide(void);
+void	squeeze(int);
+void	substitute(int);
+
+Rune La[] = { 'a', 0 };
+Rune Lr[] = { 'r', 0 };
+
+char tmp[] = "/tmp/eXXXXX";
+
+void
+main(int argc, char *argv[])
+{
+	char *p1, *p2;
+
+	notify(notifyf);
+	ARGBEGIN {
+	case 'o':
+		oflag = 1;
+		vflag = 0;
+		break;
+	} ARGEND
+
+	USED(argc);
+	if(*argv && (strcmp(*argv, "-") == 0)) {
+		argv++;
+		vflag = 0;
+	}
+	if(oflag) {
+		p1 = "/fd/1";
+		p2 = savedfile;
+		while(*p2++ = *p1++)
+			;
+		globp = La;
+	} else
+	if(*argv) {
+		p1 = *argv;
+		p2 = savedfile;
+		while(*p2++ = *p1++)
+			if(p2 >= &savedfile[sizeof(savedfile)])
+				p2--;
+		globp = Lr;
+	}
+	zero = malloc((nlall+5)*sizeof(int*));
+	tfname = mktemp(tmp);
+	init();
+	setjmp(savej);
+	commands();
+	quit();
+}
+
+void
+commands(void)
+{
+	int *a1, c, temp;
+	char lastsep;
+	Dir *d;
+
+	for(;;) {
+		if(pflag) {
+			pflag = 0;
+			addr1 = addr2 = dot;
+			printcom();
+		}
+		c = '\n';
+		for(addr1 = 0;;) {
+			lastsep = c;
+			a1 = address();
+			c = getchr();
+			if(c != ',' && c != ';')
+				break;
+			if(lastsep == ',')
+				error(Q);
+			if(a1 == 0) {
+				a1 = zero+1;
+				if(a1 > dol)
+					a1--;
+			}
+			addr1 = a1;
+			if(c == ';')
+				dot = a1;
+		}
+		if(lastsep != '\n' && a1 == 0)
+			a1 = dol;
+		if((addr2=a1) == 0) {
+			given = 0;
+			addr2 = dot;	
+		} else
+			given = 1;
+		if(addr1 == 0)
+			addr1 = addr2;
+		switch(c) {
+
+		case 'a':
+			add(0);
+			continue;
+
+		case 'b':
+			nonzero();
+			browse();
+			continue;
+
+		case 'c':
+			nonzero();
+			newline();
+			rdelete(addr1, addr2);
+			append(gettty, addr1-1);
+			continue;
+
+		case 'd':
+			nonzero();
+			newline();
+			rdelete(addr1, addr2);
+			continue;
+
+		case 'E':
+			fchange = 0;
+			c = 'e';
+		case 'e':
+			setnoaddr();
+			if(vflag && fchange) {
+				fchange = 0;
+				error(Q);
+			}
+			filename(c);
+			init();
+			addr2 = zero;
+			goto caseread;
+
+		case 'f':
+			setnoaddr();
+			filename(c);
+			putst(savedfile);
+			continue;
+
+		case 'g':
+			global(1);
+			continue;
+
+		case 'i':
+			add(-1);
+			continue;
+
+
+		case 'j':
+			if(!given)
+				addr2++;
+			newline();
+			join();
+			continue;
+
+		case 'k':
+			nonzero();
+			c = getchr();
+			if(c < 'a' || c > 'z')
+				error(Q);
+			newline();
+			names[c-'a'] = *addr2 & ~01;
+			anymarks |= 01;
+			continue;
+
+		case 'm':
+			move(0);
+			continue;
+
+		case 'n':
+			listn++;
+			newline();
+			printcom();
+			continue;
+
+		case '\n':
+			if(a1==0) {
+				a1 = dot+1;
+				addr2 = a1;
+				addr1 = a1;
+			}
+			if(lastsep==';')
+				addr1 = a1;
+			printcom();
+			continue;
+
+		case 'l':
+			listf++;
+		case 'p':
+		case 'P':
+			newline();
+			printcom();
+			continue;
+
+		case 'Q':
+			fchange = 0;
+		case 'q':
+			setnoaddr();
+			newline();
+			quit();
+
+		case 'r':
+			filename(c);
+		caseread:
+			if((io=open(file, OREAD)) < 0) {
+				lastc = '\n';
+				error(file);
+			}
+			if((d = dirfstat(io)) != nil){
+				if(d->mode & DMAPPEND)
+					print("warning: %s is append only\n", file);
+				free(d);
+			}
+			Binit(&iobuf, io, OREAD);
+			setwide();
+			squeeze(0);
+			c = zero != dol;
+			append(getfile, addr2);
+			exfile(OREAD);
+
+			fchange = c;
+			continue;
+
+		case 's':
+			nonzero();
+			substitute(globp != 0);
+			continue;
+
+		case 't':
+			move(1);
+			continue;
+
+		case 'u':
+			nonzero();
+			newline();
+			if((*addr2&~01) != subnewa)
+				error(Q);
+			*addr2 = subolda;
+			dot = addr2;
+			continue;
+
+		case 'v':
+			global(0);
+			continue;
+
+		case 'W':
+			wrapp++;
+		case 'w':
+			setwide();
+			squeeze(dol>zero);
+			temp = getchr();
+			if(temp != 'q' && temp != 'Q') {
+				peekc = temp;
+				temp = 0;
+			}
+			filename(c);
+			if(!wrapp ||
+			  ((io = open(file, OWRITE)) == -1) ||
+			  ((seek(io, 0L, 2)) == -1))
+				if((io = create(file, OWRITE, 0666)) < 0)
+					error(file);
+			Binit(&iobuf, io, OWRITE);
+			wrapp = 0;
+			if(dol > zero)
+				putfile();
+			exfile(OWRITE);
+			if(addr1<=zero+1 && addr2==dol)
+				fchange = 0;
+			if(temp == 'Q')
+				fchange = 0;
+			if(temp)
+				quit();
+			continue;
+
+		case '=':
+			setwide();
+			squeeze(0);
+			newline();
+			count = addr2 - zero;
+			putd();
+			putchr(L'\n');
+			continue;
+
+		case '!':
+			callunix();
+			continue;
+
+		case EOF:
+			return;
+
+		}
+		error(Q);
+	}
+}
+
+void
+printcom(void)
+{
+	int *a1;
+
+	nonzero();
+	a1 = addr1;
+	do {
+		if(listn) {
+			count = a1-zero;
+			putd();
+			putchr(L'\t');
+		}
+		putshst(getline(*a1++));
+	} while(a1 <= addr2);
+	dot = addr2;
+	listf = 0;
+	listn = 0;
+	pflag = 0;
+}
+
+int*
+address(void)
+{
+	int sign, *a, opcnt, nextopand, *b, c;
+
+	nextopand = -1;
+	sign = 1;
+	opcnt = 0;
+	a = dot;
+	do {
+		do {
+			c = getchr();
+		} while(c == ' ' || c == '\t');
+		if(c >= '0' && c <= '9') {
+			peekc = c;
+			if(!opcnt)
+				a = zero;
+			a += sign*getnum();
+		} else
+		switch(c) {
+		case '$':
+			a = dol;
+		case '.':
+			if(opcnt)
+				error(Q);
+			break;
+		case '\'':
+			c = getchr();
+			if(opcnt || c < 'a' || c > 'z')
+				error(Q);
+			a = zero;
+			do {
+				a++;
+			} while(a <= dol && names[c-'a'] != (*a & ~01));
+			break;
+		case '?':
+			sign = -sign;
+		case '/':
+			compile(c);
+			b = a;
+			for(;;) {
+				a += sign;
+				if(a <= zero)
+					a = dol;
+				if(a > dol)
+					a = zero;
+				if(match(a))
+					break;
+				if(a == b)
+					error(Q);
+			}
+			break;
+		default:
+			if(nextopand == opcnt) {
+				a += sign;
+				if(a < zero || dol < a)
+					continue;       /* error(Q); */
+			}
+			if(c != '+' && c != '-' && c != '^') {
+				peekc = c;
+				if(opcnt == 0)
+					a = 0;
+				return a;
+			}
+			sign = 1;
+			if(c != '+')
+				sign = -sign;
+			nextopand = ++opcnt;
+			continue;
+		}
+		sign = 1;
+		opcnt++;
+	} while(zero <= a && a <= dol);
+	error(Q);
+	return 0;
+}
+
+int
+getnum(void)
+{
+	int r, c;
+
+	r = 0;
+	for(;;) {
+		c = getchr();
+		if(c < '0' || c > '9')
+			break;
+		r = r*10 + (c-'0');
+	}
+	peekc = c;
+	return r;
+}
+
+void
+setwide(void)
+{
+	if(!given) {
+		addr1 = zero + (dol>zero);
+		addr2 = dol;
+	}
+}
+
+void
+setnoaddr(void)
+{
+	if(given)
+		error(Q);
+}
+
+void
+nonzero(void)
+{
+	squeeze(1);
+}
+
+void
+squeeze(int i)
+{
+	if(addr1 < zero+i || addr2 > dol || addr1 > addr2)
+		error(Q);
+}
+
+void
+newline(void)
+{
+	int c;
+
+	c = getchr();
+	if(c == '\n' || c == EOF)
+		return;
+	if(c == 'p' || c == 'l' || c == 'n') {
+		pflag++;
+		if(c == 'l')
+			listf++;
+		else
+		if(c == 'n')
+			listn++;
+		c = getchr();
+		if(c == '\n')
+			return;
+	}
+	error(Q);
+}
+
+void
+filename(int comm)
+{
+	char *p1, *p2;
+	Rune rune;
+	int c;
+
+	count = 0;
+	c = getchr();
+	if(c == '\n' || c == EOF) {
+		p1 = savedfile;
+		if(*p1 == 0 && comm != 'f')
+			error(Q);
+		p2 = file;
+		while(*p2++ = *p1++)
+			;
+		return;
+	}
+	if(c != ' ')
+		error(Q);
+	while((c=getchr()) == ' ')
+		;
+	if(c == '\n')
+		error(Q);
+	p1 = file;
+	do {
+		if(p1 >= &file[sizeof(file)-6] || c == ' ' || c == EOF)
+			error(Q);
+		rune = c;
+		p1 += runetochar(p1, &rune);
+	} while((c=getchr()) != '\n');
+	*p1 = 0;
+	if(savedfile[0] == 0 || comm == 'e' || comm == 'f') {
+		p1 = savedfile;
+		p2 = file;
+		while(*p1++ = *p2++)
+			;
+	}
+}
+
+void
+exfile(int om)
+{
+
+	if(om == OWRITE)
+		if(Bflush(&iobuf) < 0)
+			error(Q);
+	close(io);
+	io = -1;
+	if(vflag) {
+		putd();
+		putchr(L'\n');
+	}
+}
+
+void
+error1(char *s)
+{
+	int c;
+
+	wrapp = 0;
+	listf = 0;
+	listn = 0;
+	count = 0;
+	seek(0, 0, 2);
+	pflag = 0;
+	if(globp)
+		lastc = '\n';
+	globp = 0;
+	peekc = lastc;
+	if(lastc)
+		for(;;) {
+			c = getchr();
+			if(c == '\n' || c == EOF)
+				break;
+		}
+	if(io > 0) {
+		close(io);
+		io = -1;
+	}
+	putchr(L'?');
+	putst(s);
+}
+
+void
+error(char *s)
+{
+	error1(s);
+	longjmp(savej, 1);
+}
+
+void
+rescue(void)
+{
+	rescuing = 1;
+	if(dol > zero) {
+		addr1 = zero+1;
+		addr2 = dol;
+		io = create("ed.hup", OWRITE, 0666);
+		if(io > 0){
+			Binit(&iobuf, io, OWRITE);
+			putfile();
+		}
+	}
+	fchange = 0;
+	quit();
+}
+
+void
+notifyf(void *a, char *s)
+{
+	if(strcmp(s, "interrupt") == 0){
+		if(rescuing || waiting)
+			noted(NCONT);
+		putchr(L'\n');
+		lastc = '\n';
+		error1(Q);
+		notejmp(a, savej, 0);
+	}
+	if(strcmp(s, "hangup") == 0){
+		if(rescuing)
+			noted(NDFLT);
+		rescue();
+	}
+	fprint(2, "ed: note: %s\n", s);
+	abort();
+}
+
+int
+getchr(void)
+{
+	char s[UTFmax];
+	int i;
+	Rune r;
+
+	if(lastc = peekc) {
+		peekc = 0;
+		return lastc;
+	}
+	if(globp) {
+		if((lastc=*globp++) != 0)
+			return lastc;
+		globp = 0;
+		return EOF;
+	}
+	for(i=0;;) {
+		if(read(0, s+i, 1) <= 0)
+			return lastc = EOF;
+		i++;
+		if(fullrune(s, i))
+			break;
+		
+	}
+	chartorune(&r, s);
+	lastc = r;
+	return lastc;
+}
+
+int
+gety(void)
+{
+	int c;
+	Rune *gf, *p;
+
+	p = linebuf;
+	gf = globp;
+	for(;;) {
+		c = getchr();
+		if(c == '\n') {
+			*p = 0;
+			return 0;
+		}
+		if(c == EOF) {
+			if(gf)
+				peekc = c;
+			return c;
+		}
+		if(c == 0)
+			continue;
+		*p++ = c;
+		if(p >= &linebuf[LBSIZE-2])
+			error(Q);
+	}
+	return 0;
+}
+
+int
+gettty(void)
+{
+	int rc;
+
+	rc = gety();
+	if(rc)
+		return rc;
+	if(linebuf[0] == '.' && linebuf[1] == 0)
+		return EOF;
+	return 0;
+}
+
+int
+getfile(void)
+{
+	int c;
+	Rune *lp;
+
+	lp = linebuf;
+	do {
+		c = Bgetrune(&iobuf);
+		if(c < 0) {
+			if(lp > linebuf) {
+				putst("'\\n' appended");
+				c = '\n';
+			} else
+				return EOF;
+		}
+		if(lp >= &linebuf[LBSIZE]) {
+			lastc = '\n';
+			error(Q);
+		}
+		*lp++ = c;
+		count++;
+	} while(c != '\n');
+	lp[-1] = 0;
+	return 0;
+}
+
+void
+putfile(void)
+{
+	int *a1;
+	Rune *lp;
+	long c;
+
+	a1 = addr1;
+	do {
+		lp = getline(*a1++);
+		for(;;) {
+			count++;
+			c = *lp++;
+			if(c == 0) {
+				if(Bputrune(&iobuf, '\n') < 0)
+					error(Q);
+				break;
+			}
+			if(Bputrune(&iobuf, c) < 0)
+				error(Q);
+		}
+	} while(a1 <= addr2);
+	if(Bflush(&iobuf) < 0)
+		error(Q);
+}
+
+int
+append(int (*f)(void), int *a)
+{
+	int *a1, *a2, *rdot, nline, tl;
+
+	nline = 0;
+	dot = a;
+	while((*f)() == 0) {
+		if((dol-zero) >= nlall) {
+			nlall += 512;
+			a1 = realloc(zero, (nlall+5)*sizeof(int*));
+			if(a1 == 0) {
+				error("MEM?");
+				rescue();
+			}
+			tl = a1 - zero;	/* relocate pointers */
+			zero += tl;
+			addr1 += tl;
+			addr2 += tl;
+			dol += tl;
+			dot += tl;
+		}
+		tl = putline();
+		nline++;
+		a1 = ++dol;
+		a2 = a1+1;
+		rdot = ++dot;
+		while(a1 > rdot)
+			*--a2 = *--a1;
+		*rdot = tl;
+	}
+	return nline;
+}
+
+void
+add(int i)
+{
+	if(i && (given || dol > zero)) {
+		addr1--;
+		addr2--;
+	}
+	squeeze(0);
+	newline();
+	append(gettty, addr2);
+}
+
+void
+browse(void)
+{
+	int forward, n;
+	static int bformat, bnum; /* 0 */
+
+	forward = 1;
+	peekc = getchr();
+	if(peekc != '\n'){
+		if(peekc == '-' || peekc == '+') {
+			if(peekc == '-')
+				forward = 0;
+			getchr();
+		}
+		n = getnum();
+		if(n > 0)
+			bpagesize = n;
+	}
+	newline();
+	if(pflag) {
+		bformat = listf;
+		bnum = listn;
+	} else {
+		listf = bformat;
+		listn = bnum;
+	}
+	if(forward) {
+		addr1 = addr2;
+		addr2 += bpagesize;
+		if(addr2 > dol)
+			addr2 = dol;
+	} else {
+		addr1 = addr2-bpagesize;
+		if(addr1 <= zero)
+			addr1 = zero+1;
+	}
+	printcom();
+}
+
+void
+callunix(void)
+{
+	int c, pid;
+	Rune rune;
+	char buf[512];
+	char *p;
+
+	setnoaddr();
+	p = buf;
+	while((c=getchr()) != EOF && c != '\n')
+		if(p < &buf[sizeof(buf) - 6]) {
+			rune = c;
+			p += runetochar(p, &rune);
+		}
+	*p = 0;
+	pid = fork();
+	if(pid == 0) {
+		execl("/bin/rc", "rc", "-c", buf, 0);
+		exits("execl failed");
+	}
+	waiting = 1;
+	while(waitpid() != pid)
+		;
+	waiting = 0;
+	if(vflag)
+		putst("!");
+}
+
+void
+quit(void)
+{
+	if(vflag && fchange && dol!=zero) {
+		fchange = 0;
+		error(Q);
+	}
+	remove(tfname);
+	exits(0);
+}
+
+void
+onquit(int sig)
+{
+	USED(sig);
+	quit();
+}
+
+void
+rdelete(int *ad1, int *ad2)
+{
+	int *a1, *a2, *a3;
+
+	a1 = ad1;
+	a2 = ad2+1;
+	a3 = dol;
+	dol -= a2 - a1;
+	do {
+		*a1++ = *a2++;
+	} while(a2 <= a3);
+	a1 = ad1;
+	if(a1 > dol)
+		a1 = dol;
+	dot = a1;
+	fchange = 1;
+}
+
+void
+gdelete(void)
+{
+	int *a1, *a2, *a3;
+
+	a3 = dol;
+	for(a1=zero; (*a1&01)==0; a1++)
+		if(a1>=a3)
+			return;
+	for(a2=a1+1; a2<=a3;) {
+		if(*a2 & 01) {
+			a2++;
+			dot = a1;
+		} else
+			*a1++ = *a2++;
+	}
+	dol = a1-1;
+	if(dot > dol)
+		dot = dol;
+	fchange = 1;
+}
+
+Rune*
+getline(int tl)
+{
+	Rune *lp, *bp;
+	int nl;
+
+	lp = linebuf;
+	bp = getblock(tl, OREAD);
+	nl = nleft;
+	tl &= ~((BLKSIZE/2) - 1);
+	while(*lp++ = *bp++) {
+		nl -= sizeof(Rune);
+		if(nl == 0) {
+			bp = getblock(tl += BLKSIZE/2, OREAD);
+			nl = nleft;
+		}
+	}
+	return linebuf;
+}
+
+int
+putline(void)
+{
+	Rune *lp, *bp;
+	int nl, tl;
+
+	fchange = 1;
+	lp = linebuf;
+	tl = tline;
+	bp = getblock(tl, OWRITE);
+	nl = nleft;
+	tl &= ~((BLKSIZE/2)-1);
+	while(*bp = *lp++) {
+		if(*bp++ == '\n') {
+			bp[-1] = 0;
+			linebp = lp;
+			break;
+		}
+		nl -= sizeof(Rune);
+		if(nl == 0) {
+			tl += BLKSIZE/2;
+			bp = getblock(tl, OWRITE);
+			nl = nleft;
+		}
+	}
+	nl = tline;
+	tline += ((lp-linebuf) + 03) & 077776;
+	return nl;
+}
+
+void
+blkio(int b, uchar *buf, int isread)
+{
+	int n;
+
+	seek(tfile, b*BLKSIZE, 0);
+	if(isread)
+		n = read(tfile, buf, BLKSIZE);
+	else
+		n = write(tfile, buf, BLKSIZE);
+	if(n != BLKSIZE)
+		error(T);
+}
+
+Rune*
+getblock(int atl, int iof)
+{
+	int bno, off;
+	
+	static uchar ibuff[BLKSIZE];
+	static uchar obuff[BLKSIZE];
+
+	bno = atl / (BLKSIZE/2);
+	off = (atl<<1) & (BLKSIZE-1) & ~03;
+	if(bno >= NBLK) {
+		lastc = '\n';
+		error(T);
+	}
+	nleft = BLKSIZE - off;
+	if(bno == iblock) {
+		ichanged |= iof;
+		return (Rune*)(ibuff+off);
+	}
+	if(bno == oblock)
+		return (Rune*)(obuff+off);
+	if(iof == OREAD) {
+		if(ichanged)
+			blkio(iblock, ibuff, 0);
+		ichanged = 0;
+		iblock = bno;
+		blkio(bno, ibuff, 1);
+		return (Rune*)(ibuff+off);
+	}
+	if(oblock >= 0)
+		blkio(oblock, obuff, 0);
+	oblock = bno;
+	return (Rune*)(obuff+off);
+}
+
+void
+init(void)
+{
+	int *markp;
+
+	close(tfile);
+	tline = 2;
+	for(markp = names; markp < &names[26]; )
+		*markp++ = 0;
+	subnewa = 0;
+	anymarks = 0;
+	iblock = -1;
+	oblock = -1;
+	ichanged = 0;
+	if((tfile = create(tfname, ORDWR, 0600)) < 0){
+		error1(T);
+		exits(0);
+	}
+	dot = dol = zero;
+}
+
+void
+global(int k)
+{
+	Rune *gp, globuf[GBSIZE];
+	int c, *a1;
+
+	if(globp)
+		error(Q);
+	setwide();
+	squeeze(dol > zero);
+	c = getchr();
+	if(c == '\n')
+		error(Q);
+	compile(c);
+	gp = globuf;
+	while((c=getchr()) != '\n') {
+		if(c == EOF)
+			error(Q);
+		if(c == '\\') {
+			c = getchr();
+			if(c != '\n')
+				*gp++ = '\\';
+		}
+		*gp++ = c;
+		if(gp >= &globuf[GBSIZE-2])
+			error(Q);
+	}
+	if(gp == globuf)
+		*gp++ = 'p';
+	*gp++ = '\n';
+	*gp = 0;
+	for(a1=zero; a1<=dol; a1++) {
+		*a1 &= ~01;
+		if(a1 >= addr1 && a1 <= addr2 && match(a1) == k)
+			*a1 |= 01;
+	}
+
+	/*
+	 * Special case: g/.../d (avoid n^2 algorithm)
+	 */
+	if(globuf[0] == 'd' && globuf[1] == '\n' && globuf[2] == 0) {
+		gdelete();
+		return;
+	}
+	for(a1=zero; a1<=dol; a1++) {
+		if(*a1 & 01) {
+			*a1 &= ~01;
+			dot = a1;
+			globp = globuf;
+			commands();
+			a1 = zero;
+		}
+	}
+}
+
+void
+join(void)
+{
+	Rune *gp, *lp;
+	int *a1;
+
+	nonzero();
+	gp = genbuf;
+	for(a1=addr1; a1<=addr2; a1++) {
+		lp = getline(*a1);
+		while(*gp = *lp++)
+			if(gp++ >= &genbuf[LBSIZE-2])
+				error(Q);
+	}
+	lp = linebuf;
+	gp = genbuf;
+	while(*lp++ = *gp++)
+		;
+	*addr1 = putline();
+	if(addr1 < addr2)
+		rdelete(addr1+1, addr2);
+	dot = addr1;
+}
+
+void
+substitute(int inglob)
+{
+	int *mp, *a1, nl, gsubf, n;
+
+	n = getnum();	/* OK even if n==0 */
+	gsubf = compsub();
+	for(a1 = addr1; a1 <= addr2; a1++) {
+		if(match(a1)){
+			int *ozero;
+			int m = n;
+
+			do {
+				int span = loc2-loc1;
+
+				if(--m <= 0) {
+					dosub();
+					if(!gsubf)
+						break;
+					if(span == 0) {	/* null RE match */
+						if(*loc2 == 0)
+							break;
+						loc2++;
+					}
+				}
+			} while(match(0));
+			if(m <= 0) {
+				inglob |= 01;
+				subnewa = putline();
+				*a1 &= ~01;
+				if(anymarks) {
+					for(mp=names; mp<&names[26]; mp++)
+						if(*mp == *a1)
+							*mp = subnewa;
+				}
+				subolda = *a1;
+				*a1 = subnewa;
+				ozero = zero;
+				nl = append(getsub, a1);
+				addr2 += nl;
+				nl += zero-ozero;
+				a1 += nl;
+			}
+		}
+	}
+	if(inglob == 0)
+		error(Q);
+}
+
+int
+compsub(void)
+{
+	int seof, c;
+	Rune *p;
+
+	seof = getchr();
+	if(seof == '\n' || seof == ' ')
+		error(Q);
+	compile(seof);
+	p = rhsbuf;
+	for(;;) {
+		c = getchr();
+		if(c == '\\') {
+			c = getchr();
+			*p++ = ESCFLG;
+			if(p >= &rhsbuf[LBSIZE/2])
+				error(Q);
+		} else
+		if(c == '\n' && (!globp || !globp[0])) {
+			peekc = c;
+			pflag++;
+			break;
+		} else
+		if(c == seof)
+			break;
+		*p++ = c;
+		if(p >= &rhsbuf[LBSIZE/2])
+			error(Q);
+	}
+	*p = 0;
+	peekc = getchr();
+	if(peekc == 'g') {
+		peekc = 0;
+		newline();
+		return 1;
+	}
+	newline();
+	return 0;
+}
+
+int
+getsub(void)
+{
+	Rune *p1, *p2;
+
+	p1 = linebuf;
+	if((p2 = linebp) == 0)
+		return EOF;
+	while(*p1++ = *p2++)
+		;
+	linebp = 0;
+	return 0;
+}
+
+void
+dosub(void)
+{
+	Rune *lp, *sp, *rp;
+	int c, n;
+
+	lp = linebuf;
+	sp = genbuf;
+	rp = rhsbuf;
+	while(lp < loc1)
+		*sp++ = *lp++;
+	while(c = *rp++) {
+		if(c == '&'){
+			sp = place(sp, loc1, loc2);
+			continue;
+		}
+		if(c == ESCFLG && (c = *rp++) >= '1' && c < MAXSUB+'0') {
+			n = c-'0';
+			if(subexp[n].s.rsp && subexp[n].e.rep) {
+				sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
+				continue;
+			}
+			error(Q);
+		}
+		*sp++ = c;
+		if(sp >= &genbuf[LBSIZE])
+			error(Q);
+	}
+	lp = loc2;
+	loc2 = sp - genbuf + linebuf;
+	while(*sp++ = *lp++)
+		if(sp >= &genbuf[LBSIZE])
+			error(Q);
+	lp = linebuf;
+	sp = genbuf;
+	while(*lp++ = *sp++)
+		;
+}
+
+Rune*
+place(Rune *sp, Rune *l1, Rune *l2)
+{
+
+	while(l1 < l2) {
+		*sp++ = *l1++;
+		if(sp >= &genbuf[LBSIZE])
+			error(Q);
+	}
+	return sp;
+}
+
+void
+move(int cflag)
+{
+	int *adt, *ad1, *ad2;
+
+	nonzero();
+	if((adt = address())==0)	/* address() guarantees addr is in range */
+		error(Q);
+	newline();
+	if(cflag) {
+		int *ozero, delta;
+		ad1 = dol;
+		ozero = zero;
+		append(getcopy, ad1++);
+		ad2 = dol;
+		delta = zero - ozero;
+		ad1 += delta;
+		adt += delta;
+	} else {
+		ad2 = addr2;
+		for(ad1 = addr1; ad1 <= ad2;)
+			*ad1++ &= ~01;
+		ad1 = addr1;
+	}
+	ad2++;
+	if(adt<ad1) {
+		dot = adt + (ad2-ad1);
+		if((++adt)==ad1)
+			return;
+		reverse(adt, ad1);
+		reverse(ad1, ad2);
+		reverse(adt, ad2);
+	} else
+	if(adt >= ad2) {
+		dot = adt++;
+		reverse(ad1, ad2);
+		reverse(ad2, adt);
+		reverse(ad1, adt);
+	} else
+		error(Q);
+	fchange = 1;
+}
+
+void
+reverse(int *a1, int *a2)
+{
+	int t;
+
+	for(;;) {
+		t = *--a2;
+		if(a2 <= a1)
+			return;
+		*a2 = *a1;
+		*a1++ = t;
+	}
+}
+
+int
+getcopy(void)
+{
+	if(addr1 > addr2)
+		return EOF;
+	getline(*addr1++);
+	return 0;
+}
+
+void
+compile(int eof)
+{
+	Rune c;
+	char *ep;
+	char expbuf[ESIZE];
+
+	if((c = getchr()) == '\n') {
+		peekc = c;
+		c = eof;
+	}
+	if(c == eof) {
+		if(!pattern)
+			error(Q);
+		return;
+	}
+	if(pattern) {
+		free(pattern);
+		pattern = 0;
+	}
+	ep = expbuf;
+	do {
+		if(c == '\\') {
+			if(ep >= expbuf+sizeof(expbuf)) {
+				error(Q);
+				return;
+			}
+			ep += runetochar(ep, &c);
+			if((c = getchr()) == '\n') {
+				error(Q);
+				return;
+			}
+		}
+		if(ep >= expbuf+sizeof(expbuf)) {
+			error(Q);
+			return;
+		}
+		ep += runetochar(ep, &c);
+	} while((c = getchr()) != eof && c != '\n');
+	if(c == '\n')
+		peekc = c;
+	*ep = 0;
+	pattern = regcomp(expbuf);
+}
+
+int
+match(int *addr)
+{
+	if(!pattern)
+		return 0;
+	if(addr){
+		if(addr == zero)
+			return 0;
+		subexp[0].s.rsp = getline(*addr);
+	} else
+		subexp[0].s.rsp = loc2;
+	subexp[0].e.rep = 0;
+	if(rregexec(pattern, linebuf, subexp, MAXSUB)) {
+		loc1 = subexp[0].s.rsp;
+		loc2 = subexp[0].e.rep;
+		return 1;
+	}
+	loc1 = loc2 = 0;
+	return 0;
+	
+}
+
+void
+putd(void)
+{
+	int r;
+
+	r = count%10;
+	count /= 10;
+	if(count)
+		putd();
+	putchr(r + L'0');
+}
+
+void
+putst(char *sp)
+{
+	Rune r;
+
+	col = 0;
+	for(;;) {
+		sp += chartorune(&r, sp);
+		if(r == 0)
+			break;
+		putchr(r);
+	}
+	putchr(L'\n');
+}
+
+void
+putshst(Rune *sp)
+{
+	col = 0;
+	while(*sp)
+		putchr(*sp++);
+	putchr(L'\n');
+}
+
+void
+putchr(int ac)
+{
+	char *lp;
+	int c;
+	Rune rune;
+
+	lp = linp;
+	c = ac;
+	if(listf) {
+		if(c == '\n') {
+			if(linp != line && linp[-1] == ' ') {
+				*lp++ = '\\';
+				*lp++ = 'n';
+			}
+		} else {
+			if(col > (72-6-2)) {
+				col = 8;
+				*lp++ = '\\';
+				*lp++ = '\n';
+				*lp++ = '\t';
+			}
+			col++;
+			if(c=='\b' || c=='\t' || c=='\\') {
+				*lp++ = '\\';
+				if(c == '\b')
+					c = 'b';
+				else
+				if(c == '\t')
+					c = 't';
+				col++;
+			} else
+			if(c<' ' || c>='\177') {
+				*lp++ = '\\';
+				*lp++ = 'x';
+				*lp++ =  hex[c>>12];
+				*lp++ =  hex[c>>8&0xF];
+				*lp++ =  hex[c>>4&0xF];
+				c     =  hex[c&0xF];
+				col += 5;
+			}
+		}
+	}
+
+	rune = c;
+	lp += runetochar(lp, &rune);
+
+	if(c == '\n' || lp >= &line[sizeof(line)-5]) {
+		linp = line;
+		write(oflag? 2: 1, line, lp-line);
+		return;
+	}
+	linp = lp;
+}
+
+char*
+mktemp(char *as)
+{
+	char *s;
+	unsigned pid;
+	int i;
+
+	pid = getpid();
+	s = as;
+	while(*s++)
+		;
+	s--;
+	while(*--s == 'X') {
+		*s = pid % 10 + '0';
+		pid /= 10;
+	}
+	s++;
+	i = 'a';
+	while(access(as, 0) != -1) {
+		if(i == 'z')
+			return "/";
+		*s = i++;
+	}
+	return as;
+}
+
+void
+regerror(char *s)
+{
+	USED(s);
+	error(Q);
+}
blob - /dev/null
blob + 0232d4e543a6d2ec7282f3ef2d67e2e0dc1448aa (mode 644)
--- /dev/null
+++ src/cmd/factor.c
@@ -0,0 +1,96 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define	whsiz	(sizeof(wheel)/sizeof(wheel[0]))
+
+double	wheel[] =
+{
+	 2,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,
+};
+
+Biobuf	bin;
+
+void	factor(double);
+
+void
+main(int argc, char *argv[])
+{
+	double n;
+	int i;
+	char *l;
+
+	if(argc > 1) {
+		for(i=1; i<argc; i++) {
+			n = atof(argv[i]);
+			factor(n);
+		}
+		exits(0);
+	}
+
+	Binit(&bin, 0, OREAD);
+	for(;;) {
+		l = Brdline(&bin, '\n');
+		if(l ==  0)
+			break;
+		n = atof(l);
+		if(n <= 0)
+			break;
+		factor(n);
+	}
+	exits(0);
+}
+
+void
+factor(double n)
+{
+	double quot, d, s;
+	int i;
+
+	print("%.0f\n", n);
+	if(n == 0)
+		return;
+	s = sqrt(n) + 1;
+	while(modf(n/2, &quot) == 0) {
+		print("     2\n");
+		n = quot;
+		s = sqrt(n) + 1;
+	}
+	while(modf(n/3, &quot) == 0) {
+		print("     3\n");
+		n = quot;
+		s = sqrt(n) + 1;
+	}
+	while(modf(n/5, &quot) == 0) {
+		print("     5\n");
+		n = quot;
+		s = sqrt(n) + 1;
+	}
+	while(modf(n/7, &quot) == 0) {
+		print("     7\n");
+		n = quot;
+		s = sqrt(n) + 1;
+	}
+	d = 1;
+	for(i=1;;) {
+		d += wheel[i];
+		while(modf(n/d, &quot) == 0) {
+			print("     %.0f\n", d);
+			n = quot;
+			s = sqrt(n) + 1;
+		}
+		i++;
+		if(i >= whsiz) {
+			i = 0;
+			if(d > s)
+				break;
+		}
+	}
+	if(n > 1)
+		print("     %.0f\n",n);
+	print("\n");
+}
blob - /dev/null
blob + dfc1e8cf372b6efee73716e232160971b3e87c53 (mode 644)
--- /dev/null
+++ src/cmd/freq.c
@@ -0,0 +1,111 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+long	count[1<<16];
+Biobuf	bout;
+
+void	freq(int, char*);
+long	flag;
+enum
+{
+	Fdec	= 1<<0,
+	Fhex	= 1<<1,
+	Foct	= 1<<2,
+	Fchar	= 1<<3,
+	Frune	= 1<<4,
+};
+
+void
+main(int argc, char *argv[])
+{
+	int f, i;
+
+	flag = 0;
+	Binit(&bout, 1, OWRITE);
+	ARGBEGIN{
+	default:
+		fprint(2, "freq: unknown option %c\n", ARGC());
+		exits("usage");
+	case 'd':
+		flag |= Fdec;
+		break;
+	case 'x':
+		flag |= Fhex;
+		break;
+	case 'o':
+		flag |= Foct;
+		break;
+	case 'c':
+		flag |= Fchar;
+		break;
+	case 'r':
+		flag |= Frune;
+		break;
+	}ARGEND
+	if((flag&(Fdec|Fhex|Foct|Fchar)) == 0)
+		flag |= Fdec | Fhex | Foct | Fchar;
+	if(argc < 1) {
+		freq(0, "-");
+		exits(0);
+	}
+	for(i=0; i<argc; i++) {
+		f = open(argv[i], 0);
+		if(f < 0) {
+			fprint(2, "cannot open %s\n", argv[i]);
+			continue;
+		}
+		freq(f, argv[i]);
+		close(f);
+	}
+	exits(0);
+}
+
+void
+freq(int f, char *s)
+{
+	Biobuf bin;
+	long c, i;
+
+	memset(count, 0, sizeof(count));
+	Binit(&bin, f, OREAD);
+	if(flag & Frune) {
+		for(;;) {
+			c = Bgetrune(&bin);
+			if(c < 0)
+				break;
+			count[c]++;
+		}
+	} else {
+		for(;;) {
+			c = Bgetc(&bin);
+			if(c < 0)
+				break;
+			count[c]++;
+		}
+	}
+	Bterm(&bin);
+	if(c != Beof)
+		fprint(2, "freq: read error on %s\n", s);
+
+	for(i=0; i<nelem(count); i++) {
+		if(count[i] == 0)
+			continue;
+		if(flag & Fdec)
+			Bprint(&bout, "%3ld ", i);
+		if(flag & Foct)
+			Bprint(&bout, "%.3lo ", i);
+		if(flag & Fhex)
+			Bprint(&bout, "%.2lx ", i);
+		if(flag & Fchar) {
+			if(i <= 0x20 ||
+			   i >= 0x7f && i < 0xa0 ||
+			   i > 0xff && !(flag & Frune))
+				Bprint(&bout, "- ");
+			else
+				Bprint(&bout, "%C ", (int)i);
+		}
+		Bprint(&bout, "%8ld\n", count[i]);
+	}
+	Bflush(&bout);
+}
blob - /dev/null
blob + e8d730750632bc476b4139c1256c4c3a74288f43 (mode 644)
--- /dev/null
+++ src/cmd/fsize.c
@@ -0,0 +1,32 @@
+#include <u.h>
+#include <libc.h>
+
+void
+usage(void)
+{
+	fprint(2, "usage: fsize file...\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int i;
+	Dir *d;
+
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND
+	if(argc == 0)
+		usage();
+
+	for(i=0; i<argc; i++){
+		if((d = dirstat(argv[i])) == nil)
+			fprint(2, "dirstat %s: %r", argv[i]);
+		else{
+			print("%s: %lld\n", argv[i], d->length);
+			free(d);
+		}
+	}
+}
blob - /dev/null
blob + 47326782d7b024bf6dec248157b6413d9ef251cc (mode 644)
--- /dev/null
+++ src/cmd/idiff.c
@@ -0,0 +1,335 @@
+/*
+ * interactive diff, inspired/stolen from
+ * kernighan and pike, _unix programming environment_.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+int diffbflag;
+int diffwflag;
+
+void copy(Biobuf*, char*, Biobuf*, char*);
+void idiff(Biobuf*, char*, Biobuf*, char*, Biobuf*, char*, Biobuf*, char*);
+int opentemp(char*, int, long);
+void rundiff(char*, char*, int);
+
+void
+usage(void)
+{
+	fprint(2, "usage: idiff [-bw] file1 file2\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int fd, ofd;
+	char diffout[40], idiffout[40];
+	Biobuf *b1, *b2, bdiff, bout, bstdout;
+	Dir *d;
+
+	ARGBEGIN{
+	default:
+		usage();
+	case 'b':
+		diffbflag++;
+		break;
+	case 'w':
+		diffwflag++;
+		break;
+	}ARGEND
+
+	if(argc != 2)
+		usage();
+
+	if((d = dirstat(argv[0])) == nil)
+		sysfatal("stat %s: %r", argv[0]);
+	if(d->mode&DMDIR)
+		sysfatal("%s is a directory", argv[0]);
+	free(d);
+	if((d = dirstat(argv[1])) == nil)
+		sysfatal("stat %s: %r", argv[1]);
+	if(d->mode&DMDIR)
+		sysfatal("%s is a directory", argv[1]);
+	free(d);
+
+	if((b1 = Bopen(argv[0], OREAD)) == nil)
+		sysfatal("open %s: %r", argv[0]);
+	if((b2 = Bopen(argv[1], OREAD)) == nil)
+		sysfatal("open %s: %r", argv[1]);
+
+	strcpy(diffout, "/tmp/idiff.XXXXXX");
+	fd = opentemp(diffout, ORDWR|ORCLOSE, 0);
+	strcpy(idiffout, "/tmp/idiff.XXXXXX");
+	ofd = opentemp(idiffout, ORDWR|ORCLOSE, 0);
+	rundiff(argv[0], argv[1], fd);
+	seek(fd, 0, 0);
+	Binit(&bdiff, fd, OREAD);
+	Binit(&bout, ofd, OWRITE);
+	idiff(b1, argv[0], b2, argv[1], &bdiff, diffout, &bout, idiffout);
+	Bterm(&bdiff);
+	Bflush(&bout);
+	seek(ofd, 0, 0);
+	Binit(&bout, ofd, OREAD);
+	Binit(&bstdout, 1, OWRITE);
+	copy(&bout, idiffout, &bstdout, "<stdout>");
+	exits(nil);
+}
+
+int
+opentemp(char *template, int mode, long perm)
+{
+	int fd, i;
+	char *p;	
+
+	p = strdup(template);
+	if(p == nil)
+		sysfatal("strdup out of memory");
+	fd = -1;
+	for(i=0; i<10; i++){
+		mktemp(p);
+		if(access(p, 0) < 0 && (fd=create(p, mode, perm)) >= 0)
+			break;
+		strcpy(p, template);
+	}
+	if(fd < 0)
+		sysfatal("could not create temporary file");
+	strcpy(template, p);
+	free(p);
+
+	return fd;
+}
+
+void
+rundiff(char *arg1, char *arg2, int outfd)
+{
+	char *arg[10], *p;
+	int narg, pid;
+	Waitmsg *w;
+
+	narg = 0;
+	arg[narg++] = "/bin/diff";
+	arg[narg++] = "-n";
+	if(diffbflag)
+		arg[narg++] = "-b";
+	if(diffwflag)
+		arg[narg++] = "-w";
+	arg[narg++] = arg1;
+	arg[narg++] = arg2;
+	arg[narg] = nil;
+
+	switch(pid = fork()){
+	case -1:
+		sysfatal("fork: %r");
+
+	case 0:
+		dup(outfd, 1);
+		close(0);
+		exec("/bin/diff", arg);
+		sysfatal("exec: %r");
+
+	default:
+		w = wait();
+		if(w==nil)
+			sysfatal("wait: %r");
+		if(w->pid != pid)
+			sysfatal("wait got unexpected pid %d", w->pid);
+		if((p = strchr(w->msg, ':')) && strcmp(p, ": some") != 0)
+			sysfatal("%s", w->msg);
+		free(w);
+	}
+}
+
+void
+runcmd(char *cmd)
+{
+	char *arg[10];
+	int narg, pid, wpid;
+
+	narg = 0;
+	arg[narg++] = "/bin/rc";
+	arg[narg++] = "-c";
+	arg[narg++] = cmd;
+	arg[narg] = nil;
+
+	switch(pid = fork()){
+	case -1:
+		sysfatal("fork: %r");
+
+	case 0:
+		exec("/bin/rc", arg);
+		sysfatal("exec: %r");
+
+	default:
+		wpid = waitpid();
+		if(wpid < 0)
+			sysfatal("wait: %r");
+		if(wpid != pid)
+			sysfatal("wait got unexpected pid %d", wpid);
+	}
+}
+
+void
+parse(char *s, int *pfrom1, int *pto1, int *pcmd, int *pfrom2, int *pto2)
+{
+	*pfrom1 = *pto1 = *pfrom2 = *pto2 = 0;
+
+	s = strchr(s, ':');
+	if(s == nil)
+		sysfatal("bad diff output0");
+	s++;
+	*pfrom1 = strtol(s, &s, 10);
+	if(*s == ','){
+		s++;
+		*pto1 = strtol(s, &s, 10);
+	}else
+		*pto1 = *pfrom1;
+	if(*s++ != ' ')
+		sysfatal("bad diff output1");
+	*pcmd = *s++;
+	if(*s++ != ' ')
+		sysfatal("bad diff output2");
+	s = strchr(s, ':');
+	if(s == nil)
+		sysfatal("bad diff output3");
+	s++;
+	*pfrom2 = strtol(s, &s, 10);
+	if(*s == ','){
+		s++;
+		*pto2 = strtol(s, &s, 10);
+	}else
+		*pto2 = *pfrom2;
+}
+
+void
+skiplines(Biobuf *b, char *name, int n)
+{
+	int i;
+
+	for(i=0; i<n; i++){
+		while(Brdline(b, '\n')==nil){
+			if(Blinelen(b) <= 0)
+				sysfatal("early end of file on %s", name);
+			Bseek(b, Blinelen(b), 1);
+		}
+	}
+}
+
+void
+copylines(Biobuf *bin, char *nin, Biobuf *bout, char *nout, int n)
+{
+	char buf[4096], *p;
+	int i, m;
+
+	for(i=0; i<n; i++){
+		while((p=Brdline(bin, '\n'))==nil){
+			if(Blinelen(bin) <= 0)
+				sysfatal("early end of file on %s", nin);
+			m = Blinelen(bin);
+			if(m > sizeof buf)
+				m = sizeof buf;
+			m = Bread(bin, buf, m);
+			if(Bwrite(bout, buf, m) != m)
+				sysfatal("error writing %s: %r", nout);
+		}
+		if(Bwrite(bout, p, Blinelen(bin)) != Blinelen(bin))
+			sysfatal("error writing %s: %r", nout);
+	}
+}
+
+void
+copy(Biobuf *bin, char *nin, Biobuf *bout, char *nout)
+{
+	char buf[4096];
+	int m;
+
+	USED(nin);
+	while((m = Bread(bin, buf, sizeof buf)) > 0)
+		if(Bwrite(bout, buf, m) != m)
+			sysfatal("error writing %s: %r", nout);
+}
+
+void
+idiff(Biobuf *b1, char *name1, Biobuf *b2, char *name2, Biobuf *bdiff, char *namediff, Biobuf *bout, char *nameout)
+{
+	char buf[256], *p;
+	int interactive, defaultanswer, cmd, diffoffset;
+	int n, from1, to1, from2, to2, nf1, nf2;
+	Biobuf berr;
+
+	nf1 = 1;
+	nf2 = 1;
+	interactive = 1;
+	defaultanswer = 0;
+	Binit(&berr, 2, OWRITE);
+	while(diffoffset = Boffset(bdiff), p = Brdline(bdiff, '\n')){
+		p[Blinelen(bdiff)-1] = '\0';
+		parse(p, &from1, &to1, &cmd, &from2, &to2);
+		p[Blinelen(bdiff)-1] = '\n';
+		n = to1-from1 + to2-from2 + 1;	/* #lines from diff */
+		if(cmd == 'c')
+			n += 2;
+		else if(cmd == 'a')
+			from1++;
+		else if(cmd == 'd')
+			from2++;
+		to1++;	/* make half-open intervals */
+		to2++;
+		if(interactive){
+			p[Blinelen(bdiff)-1] = '\0';
+			fprint(2, "%s\n", p);
+			p[Blinelen(bdiff)-1] = '\n';
+			copylines(bdiff, namediff, &berr, "<stderr>", n);
+			Bflush(&berr);
+		}else
+			skiplines(bdiff, namediff, n);
+		do{
+			if(interactive){
+				fprint(2, "? ");
+				memset(buf, 0, sizeof buf);
+				if(read(0, buf, sizeof buf - 1) < 0)
+					sysfatal("read console: %r");
+			}else
+				buf[0] = defaultanswer;
+
+			switch(buf[0]){
+			case '>':
+				copylines(b1, name1, bout, nameout, from1-nf1);
+				skiplines(b1, name1, to1-from1);
+				skiplines(b2, name2, from2-nf2);
+				copylines(b2, name2, bout, nameout, to2-from2);
+				break;
+			case '<':
+				copylines(b1, name1, bout, nameout, to1-nf1);
+				skiplines(b2, name2, to2-nf2);
+				break;
+			case '=':
+				copylines(b1, name1, bout, nameout, from1-nf1);
+				skiplines(b1, name1, to1-from1);
+				skiplines(b2, name2, to2-nf2);
+				if(Bseek(bdiff, diffoffset, 0) != diffoffset)
+					sysfatal("seek in diff output: %r");
+				copylines(bdiff, namediff, bout, nameout, n+1);
+				break;
+			case '!':
+				runcmd(buf+1);
+				break;
+			case 'q':
+				if(buf[1]=='<' || buf[1]=='>' || buf[1]=='='){
+					interactive = 0;
+					defaultanswer = buf[1];
+				}else
+					fprint(2, "must be q<, q>, or q=\n");
+				break;
+			default:
+				fprint(2, "expect: <, >, =, q<, q>, q=, !cmd\n");
+				break;
+			}
+		}while(buf[0] != '<' && buf[0] != '>' && buf[0] != '=');
+		nf1 = to1;
+		nf2 = to2;
+	}
+	copy(b1, name1, bout, nameout);
+}
blob - /dev/null
blob + 7efff9768ecb933a6c78e68047aa7864b51e1498 (mode 644)
--- /dev/null
+++ src/cmd/join.c
@@ -0,0 +1,369 @@
+/*	join F1 F2 on stuff */
+#include <u.h>
+#include <libc.h>
+#include <stdio.h>
+#include <ctype.h>
+#define F1 0
+#define F2 1
+#define F0 3
+#define	NFLD	100	/* max field per line */
+#define comp() runecmp(ppi[F1][j1],ppi[F2][j2])
+FILE *f[2];
+Rune buf[2][BUFSIZ];	/*input lines */
+Rune *ppi[2][NFLD+1];	/* pointers to fields in lines */
+Rune *s1,*s2;
+#define j1 joinj1
+#define j2 joinj2
+
+int	j1	= 1;	/* join of this field of file 1 */
+int	j2	= 1;	/* join of this field of file 2 */
+int	olist[2*NFLD];	/* output these fields */
+int	olistf[2*NFLD];	/* from these files */
+int	no;		/* number of entries in olist */
+Rune	sep1	= ' ';	/* default field separator */
+Rune	sep2	= '\t';
+char *sepstr=" ";
+int	discard;	/* count of truncated lines */
+Rune	null[BUFSIZ]/*	= L""*/;
+int	a1;
+int 	a2;
+
+char *getoptarg(int*, char***);
+void output(int, int);
+int input(int);
+void oparse(char*);
+void error(char*, char*);
+void seek1(void), seek2(void);
+Rune *strtorune(Rune *, char *);
+
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	while (argc > 1 && argv[1][0] == '-') {
+		if (argv[1][1] == '\0')
+			break;
+		switch (argv[1][1]) {
+		case '-':
+			argc--;
+			argv++;
+			goto proceed;
+		case 'a':
+			switch(*getoptarg(&argc, &argv)) {
+			case '1':
+				a1++;
+				break;
+			case '2':
+				a2++;
+				break;
+			default:
+				error("incomplete option -a","");
+			}
+			break;
+		case 'e':
+			strtorune(null, getoptarg(&argc, &argv));
+			break;
+		case 't':
+			sepstr=getoptarg(&argc, &argv);
+			chartorune(&sep1, sepstr);
+			sep2 = sep1;
+			break;
+		case 'o':
+			if(argv[1][2]!=0 ||
+			   argc>2 && strchr(argv[2],',')!=0)
+				oparse(getoptarg(&argc, &argv));
+			else for (no = 0; no<2*NFLD && argc>2; no++){
+				if (argv[2][0] == '1' && argv[2][1] == '.') {
+					olistf[no] = F1;
+					olist[no] = atoi(&argv[2][2]);
+				} else if (argv[2][0] == '2' && argv[2][1] == '.') {
+					olist[no] = atoi(&argv[2][2]);
+					olistf[no] = F2;
+				} else if (argv[2][0] == '0')
+					olistf[no] = F0;
+				else
+					break;
+				argc--;
+				argv++;
+			}
+			break;
+		case 'j':
+			if(argc <= 2)
+				break;
+			if (argv[1][2] == '1')
+				j1 = atoi(argv[2]);
+			else if (argv[1][2] == '2')
+				j2 = atoi(argv[2]);
+			else
+				j1 = j2 = atoi(argv[2]);
+			argc--;
+			argv++;
+			break;
+		case '1':
+			j1 = atoi(getoptarg(&argc, &argv));
+			break;
+		case '2':
+			j2 = atoi(getoptarg(&argc, &argv));
+			break;
+		}
+		argc--;
+		argv++;
+	}
+proceed:
+	for (i = 0; i < no; i++)
+		if (olist[i]-- > NFLD)	/* 0 origin */
+			error("field number too big in -o","");
+	if (argc != 3)
+		error("usage: join [-1 x -2 y] [-o list] file1 file2","");
+	j1--;
+	j2--;	/* everyone else believes in 0 origin */
+	s1 = ppi[F1][j1];
+	s2 = ppi[F2][j2];
+	if (strcmp(argv[1], "-") == 0)
+		f[F1] = stdin;
+	else if ((f[F1] = fopen(argv[1], "r")) == 0)
+		error("can't open %s", argv[1]);
+	if(strcmp(argv[2], "-") == 0) {
+		f[F2] = stdin;
+	} else if ((f[F2] = fopen(argv[2], "r")) == 0)
+		error("can't open %s", argv[2]);
+
+	if(ftell(f[F2]) >= 0)
+		seek2();
+	else if(ftell(f[F1]) >= 0)
+		seek1();
+	else
+		error("neither file is randomly accessible","");
+	if (discard)
+		error("some input line was truncated", "");
+	exits("");
+}
+int runecmp(Rune *a, Rune *b){
+	while(*a==*b){
+		if(*a=='\0') return 0;
+		a++;
+		b++;
+	}
+	if(*a<*b) return -1;
+	return 1;
+}
+char *runetostr(char *buf, Rune *r){
+	char *s;
+	for(s=buf;*r;r++) s+=runetochar(s, r);
+	*s='\0';
+	return buf;
+}
+Rune *strtorune(Rune *buf, char *s){
+	Rune *r;
+	for(r=buf;*s;r++) s+=chartorune(r, s);
+	*r='\0';
+	return buf;
+}
+/* lazy.  there ought to be a clean way to combine seek1 & seek2 */
+#define get1() n1=input(F1)
+#define get2() n2=input(F2)
+void
+seek2()
+{
+	int n1, n2;
+	int top2=0;
+	int bot2 = ftell(f[F2]);
+	get1();
+	get2();
+	while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) {
+		if(n1>0 && n2>0 && comp()>0 || n1==0) {
+			if(a2) output(0, n2);
+			bot2 = ftell(f[F2]);
+			get2();
+		} else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+			if(a1) output(n1, 0);
+			get1();
+		} else /*(n1>0 && n2>0 && comp()==0)*/ {
+			while(n2>0 && comp()==0) {
+				output(n1, n2);
+				top2 = ftell(f[F2]);
+				get2();
+			}
+			fseek(f[F2], bot2, 0);
+			get2();
+			get1();
+			for(;;) {
+				if(n1>0 && n2>0 && comp()==0) {
+					output(n1, n2);
+					get2();
+				} else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+					fseek(f[F2], bot2, 0);
+					get2();
+					get1();
+				} else /*(n1>0 && n2>0 && comp()>0 || n1==0)*/{
+					fseek(f[F2], top2, 0);
+					bot2 = top2;
+					get2();
+					break;
+				}
+			}
+		}
+	}
+}
+void
+seek1()
+{
+	int n1, n2;
+	int top1=0;
+	int bot1 = ftell(f[F1]);
+	get1();
+	get2();
+	while(n1>0 && n2>0 || (a1||a2) && n1+n2>0) {
+		if(n1>0 && n2>0 && comp()>0 || n1==0) {
+			if(a2) output(0, n2);
+			get2();
+		} else if(n1>0 && n2>0 && comp()<0 || n2==0) {
+			if(a1) output(n1, 0);
+			bot1 = ftell(f[F1]);
+			get1();
+		} else /*(n1>0 && n2>0 && comp()==0)*/ {
+			while(n2>0 && comp()==0) {
+				output(n1, n2);
+				top1 = ftell(f[F1]);
+				get1();
+			}
+			fseek(f[F1], bot1, 0);
+			get2();
+			get1();
+			for(;;) {
+				if(n1>0 && n2>0 && comp()==0) {
+					output(n1, n2);
+					get1();
+				} else if(n1>0 && n2>0 && comp()>0 || n1==0) {
+					fseek(f[F1], bot1, 0);
+					get2();
+					get1();
+				} else /*(n1>0 && n2>0 && comp()<0 || n2==0)*/{
+					fseek(f[F1], top1, 0);
+					bot1 = top1;
+					get1();
+					break;
+				}
+			}
+		}
+	}
+}
+
+int
+input(int n)		/* get input line and split into fields */
+{
+	register int i, c;
+	Rune *bp;
+	Rune **pp;
+	char line[BUFSIZ];
+
+	bp = buf[n];
+	pp = ppi[n];
+	if (fgets(line, BUFSIZ, f[n]) == 0)
+		return(0);
+	strtorune(bp, line);
+	i = 0;
+	do {
+		i++;
+		if (sep1 == ' ')	/* strip multiples */
+			while ((c = *bp) == sep1 || c == sep2)
+				bp++;	/* skip blanks */
+		*pp++ = bp;	/* record beginning */
+		while ((c = *bp) != sep1 && c != '\n' && c != sep2 && c != '\0')
+			bp++;
+		*bp++ = '\0';	/* mark end by overwriting blank */
+	} while (c != '\n' && c != '\0' && i < NFLD-1);
+	if (c != '\n')
+		discard++;
+
+	*pp = 0;
+	return(i);
+}
+
+void
+output(int on1, int on2)	/* print items from olist */
+{
+	int i;
+	Rune *temp;
+	char buf[BUFSIZ];
+
+	if (no <= 0) {	/* default case */
+		printf("%s", runetostr(buf, on1? ppi[F1][j1]: ppi[F2][j2]));
+		for (i = 0; i < on1; i++)
+			if (i != j1)
+				printf("%s%s", sepstr, runetostr(buf, ppi[F1][i]));
+		for (i = 0; i < on2; i++)
+			if (i != j2)
+				printf("%s%s", sepstr, runetostr(buf, ppi[F2][i]));
+		printf("\n");
+	} else {
+		for (i = 0; i < no; i++) {
+			if (olistf[i]==F0 && on1>j1)
+				temp = ppi[F1][j1];
+			else if (olistf[i]==F0 && on2>j2)
+				temp = ppi[F2][j2];
+			else {
+				temp = ppi[olistf[i]][olist[i]];
+				if(olistf[i]==F1 && on1<=olist[i] ||
+				   olistf[i]==F2 && on2<=olist[i] ||
+				   *temp==0)
+					temp = null;
+			}
+			printf("%s", runetostr(buf, temp));
+			if (i == no - 1)
+				printf("\n");
+			else
+				printf("%s", sepstr);
+		}
+	}
+}
+
+void
+error(char *s1, char *s2)
+{
+	fprintf(stderr, "join: ");
+	fprintf(stderr, s1, s2);
+	fprintf(stderr, "\n");
+	exits(s1);
+}
+
+char *
+getoptarg(int *argcp, char ***argvp)
+{
+	int argc = *argcp;
+	char **argv = *argvp;
+	if(argv[1][2] != 0)
+		return &argv[1][2];
+	if(argc<=2 || argv[2][0]=='-')
+		error("incomplete option %s", argv[1]);
+	*argcp = argc-1;
+	*argvp = ++argv;
+	return argv[1];
+}
+
+void
+oparse(char *s)
+{
+	for (no = 0; no<2*NFLD && *s; no++, s++) {
+		switch(*s) {
+		case 0:
+			return;
+		case '0':
+			olistf[no] = F0;
+			break;
+		case '1':
+		case '2':
+			if(s[1] == '.' && isdigit(s[2])) {
+				olistf[no] = *s=='1'? F1: F2;
+				olist[no] = atoi(s += 2);
+				break;
+			} /* fall thru */
+		default:
+			error("invalid -o list", "");
+		}
+		if(s[1] == ',')
+			s++;
+	}
+}
blob - /dev/null
blob + de2bee946b997fe3a8dbc0865570e897095f4d96 (mode 644)
--- /dev/null
+++ src/cmd/ls.C
@@ -0,0 +1,305 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+typedef struct NDir NDir;
+struct NDir
+{
+	Dir *d;
+	char	*prefix;
+};
+
+int	errs = 0;
+int	dflag;
+int	lflag;
+int	mflag;
+int	nflag;
+int	pflag;
+int	qflag;
+int	Qflag;
+int	rflag;
+int	sflag;
+int	tflag;
+int	uflag;
+int	Fflag;
+int	ndirbuf;
+int	ndir;
+NDir*	dirbuf;
+int	ls(char*, int);
+int	compar(NDir*, NDir*);
+char*	asciitime(long);
+char*	darwx(long);
+void	rwx(long, char*);
+void	growto(long);
+void	dowidths(Dir*);
+void	format(Dir*, char*);
+void	output(void);
+ulong	clk;
+int	swidth;			/* max width of -s size */
+int	qwidth;			/* max width of -q version */
+int	vwidth;			/* max width of dev */
+int	uwidth;			/* max width of userid */
+int	mwidth;			/* max width of muid */
+int	glwidth;		/* max width of groupid and length */
+Biobuf	bin;
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+
+	Binit(&bin, 1, OWRITE);
+	ARGBEGIN{
+	case 'F':	Fflag++; break;
+	case 'd':	dflag++; break;
+	case 'l':	lflag++; break;
+	case 'm':	mflag++; break;
+	case 'n':	nflag++; break;
+	case 'p':	pflag++; break;
+	case 'q':	qflag++; break;
+	case 'Q':	Qflag++; break;
+	case 'r':	rflag++; break;
+	case 's':	sflag++; break;
+	case 't':	tflag++; break;
+	case 'u':	uflag++; break;
+	default:	fprint(2, "usage: ls [-dlmnpqrstuFQ] [file ...]\n");
+			exits("usage");
+	}ARGEND
+
+	doquote = needsrcquote;
+	quotefmtinstall();
+	fmtinstall('M', dirmodefmt);
+
+	if(lflag)
+		clk = time(0);
+	if(argc == 0)
+		errs = ls(".", 0);
+	else for(i=0; i<argc; i++)
+		errs |= ls(argv[i], 1);
+	output();
+	exits(errs? "errors" : 0);
+}
+
+int
+ls(char *s, int multi)
+{
+	int fd;
+	long i, n;
+	char *p;
+	Dir *db;
+
+	for(;;) {
+		p = utfrrune(s, '/');
+		if(p == 0 || p[1] != 0 || p == s)
+			break;
+		*p = 0;
+	}
+	db = dirstat(s);
+	if(db == nil){
+    error:
+		fprint(2, "ls: %s: %r\n", s);
+		return 1;
+	}
+	if(db->qid.type&QTDIR && dflag==0){
+		output();
+		fd = open(s, OREAD);
+		if(fd == -1)
+			goto error;
+		n = dirreadall(fd, &db);
+		if(n < 0)
+			goto error;
+		growto(ndir+n);
+		for(i=0; i<n; i++){
+			dirbuf[ndir+i].d = db+i;
+			dirbuf[ndir+i].prefix = multi? s : 0;
+		}
+		ndir += n;
+		close(fd);
+		output();
+	}else{
+		growto(ndir+1);
+		dirbuf[ndir].d = db;
+		dirbuf[ndir].prefix = 0;
+		p = utfrrune(s, '/');
+		if(p){
+			dirbuf[ndir].prefix = s;
+			*p = 0;
+			/* restore original name; don't use result of stat */
+			dirbuf[ndir].d->name = strdup(p+1);
+		}
+		ndir++;
+	}
+	return 0;
+}
+
+void
+output(void)
+{
+	int i;
+	char buf[4096];
+	char *s;
+
+	if(!nflag)
+		qsort(dirbuf, ndir, sizeof dirbuf[0], (int (*)(const void*, const void*))compar);
+	for(i=0; i<ndir; i++)
+		dowidths(dirbuf[i].d);
+	for(i=0; i<ndir; i++) {
+		if(!pflag && (s = dirbuf[i].prefix)) {
+			if(strcmp(s, "/") ==0)	/* / is a special case */
+				s = "";
+			sprint(buf, "%s/%s", s, dirbuf[i].d->name);
+			format(dirbuf[i].d, buf);
+		} else
+			format(dirbuf[i].d, dirbuf[i].d->name);
+	}
+	ndir = 0;
+	Bflush(&bin);
+}
+
+void
+dowidths(Dir *db)
+{
+	char buf[256];
+	int n;
+
+	if(sflag) {
+		n = sprint(buf, "%llud", (db->length+1023)/1024);
+		if(n > swidth)
+			swidth = n;
+	}
+	if(qflag) {
+		n = sprint(buf, "%lud", db->qid.vers);
+		if(n > qwidth)
+			qwidth = n;
+	}
+	if(mflag) {
+		n = snprint(buf, sizeof buf, "[%s]", db->muid);
+		if(n > mwidth)
+			mwidth = n;
+	}
+	if(lflag) {
+		n = sprint(buf, "%ud", db->dev);
+		if(n > vwidth)
+			vwidth = n;
+		n = strlen(db->uid);
+		if(n > uwidth)
+			uwidth = n;
+		n = sprint(buf, "%llud", db->length);
+		n += strlen(db->gid);
+		if(n > glwidth)
+			glwidth = n;
+	}
+}
+
+char*
+fileflag(Dir *db)
+{
+	if(Fflag == 0)
+		return "";
+	if(QTDIR & db->qid.type)
+		return "/";
+	if(0111 & db->mode)
+		return "*";
+	return "";
+}
+
+void
+format(Dir *db, char *name)
+{
+	int i;
+
+	if(sflag)
+		Bprint(&bin, "%*llud ",
+			swidth, (db->length+1023)/1024);
+	if(mflag){
+		Bprint(&bin, "[%s] ", db->muid);
+		for(i=2+strlen(db->muid); i<mwidth; i++)
+			Bprint(&bin, " ");
+	}
+	if(qflag)
+		Bprint(&bin, "(%.16llux %*lud %.2ux) ",
+			db->qid.path,
+			qwidth, db->qid.vers,
+			db->qid.type);
+	if(lflag)
+		Bprint(&bin,
+			Qflag? "%M %C %*ud %*s %s %*llud %s %s\n" : "%M %C %*ud %*s %s %*llud %s %q\n",
+			db->mode, db->type,
+			vwidth, db->dev,
+			-uwidth, db->uid,
+			db->gid,
+			(int)(glwidth-strlen(db->gid)), db->length,
+			asciitime(uflag? db->atime : db->mtime), name);
+	else
+		Bprint(&bin,
+			Qflag? "%s%s\n" : "%q%s\n",
+			name, fileflag(db));
+}
+
+void
+growto(long n)
+{
+	if(n <= ndirbuf)
+		return;
+	ndirbuf = n;
+	dirbuf=(NDir *)realloc(dirbuf, ndirbuf*sizeof(NDir));
+	if(dirbuf == 0){
+		fprint(2, "ls: malloc fail\n");
+		exits("malloc fail");
+	}		
+}
+
+int
+compar(NDir *a, NDir *b)
+{
+	long i;
+	Dir *ad, *bd;
+
+	ad = a->d;
+	bd = b->d;
+
+	if(tflag){
+		if(uflag)
+			i = bd->atime-ad->atime;
+		else
+			i = bd->mtime-ad->mtime;
+	}else{
+		if(a->prefix && b->prefix){
+			i = strcmp(a->prefix, b->prefix);
+			if(i == 0)
+				i = strcmp(ad->name, bd->name);
+		}else if(a->prefix){
+			i = strcmp(a->prefix, bd->name);
+			if(i == 0)
+				i = 1;	/* a is longer than b */
+		}else if(b->prefix){
+			i = strcmp(ad->name, b->prefix);
+			if(i == 0)
+				i = -1;	/* b is longer than a */
+		}else
+			i = strcmp(ad->name, bd->name);
+	}
+	if(i == 0)
+		i = (a<b? -1 : 1);
+	if(rflag)
+		i = -i;
+	return i;
+}
+
+char*
+asciitime(long l)
+{
+	static char buf[32];
+	char *t;
+
+	t = ctime(l);
+	/* 6 months in the past or a day in the future */
+	if(l<clk-180L*24*60*60 || clk+24L*60*60<l){
+		memmove(buf, t+4, 7);		/* month and day */
+		memmove(buf+7, t+23, 5);		/* year */
+	}else
+		memmove(buf, t+4, 12);		/* skip day of week */
+	buf[12] = 0;
+	return buf;
+}
+
blob - /dev/null
blob + 8eabbf62f0c6bc31f74c6f35aee59f79f6ffc058 (mode 644)
--- /dev/null
+++ src/cmd/md5sum.C
@@ -0,0 +1,61 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <libsec.h>
+
+static int
+digestfmt(Fmt *fmt)
+{
+	char buf[MD5dlen*2+1];
+	uchar *p;
+	int i;
+
+	p = va_arg(fmt->args, uchar*);
+	for(i=0; i<MD5dlen; i++)
+		sprint(buf+2*i, "%.2ux", p[i]);
+	return fmtstrcpy(fmt, buf);
+}
+
+static void
+sum(int fd, char *name)
+{
+	int n;
+	uchar buf[8192], digest[MD5dlen];
+	DigestState *s;
+
+	s = md5(nil, 0, nil, nil);
+	while((n = read(fd, buf, sizeof buf)) > 0)
+		md5(buf, n, nil, s);
+	md5(nil, 0, digest, s);
+	if(name == nil)
+		print("%M\n", digest);
+	else
+		print("%M\t%s\n", digest, name);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, fd;
+
+	ARGBEGIN{
+	default:
+		fprint(2, "usage: md5sum [file...]\n");
+		exits("usage");
+	}ARGEND
+
+	fmtinstall('M', digestfmt);
+
+	if(argc == 0)
+		sum(0, nil);
+	else for(i = 0; i < argc; i++){
+		fd = open(argv[i], OREAD);
+		if(fd < 0){
+			fprint(2, "md5sum: can't open %s: %r\n", argv[i]);
+			continue;
+		}
+		sum(fd, argv[i]);
+		close(fd);
+	}
+	exits(nil);
+}
blob - /dev/null
blob + 5bfa67735a08d26d1f5ed4952ae4bb3e4cd26c23 (mode 644)
--- /dev/null
+++ src/cmd/mkdir.C
@@ -0,0 +1,26 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+	int i, f;
+	char *e;
+
+	e = nil;
+	for(i=1; i<argc; i++){
+		if(access(argv[i], 0) == AEXIST){
+			fprint(2, "mkdir: %s already exists\n", argv[i]);
+			e = "error";
+			continue;
+		}
+		f = create(argv[i], OREAD, DMDIR | 0777L);
+		if(f < 0){
+			fprint(2, "mkdir: can't create %s: %r\n", argv[i]);
+			e = "error";
+			continue;
+		}
+		close(f);
+	}
+	exits(e);
+}
blob - /dev/null
blob + 72a2320a7c8a87e5a74813db844d23a0e0b7583b (mode 644)
--- /dev/null
+++ src/cmd/mkfile
@@ -0,0 +1,13 @@
+PLAN9=../..
+<$PLAN9/src/mkhdr
+
+TARG=`ls *.c | sed 's/\.c//'`
+LDFLAGS=$LDFLAGS -lsec -lregexp9 -l9 -lbio -lfmt -lutf
+
+<$PLAN9/src/mkmany
+
+BUGGERED='CVS|oplumb|plumb|plumb2|mk|vac'
+DIRS=`ls -l |sed -n 's/^d.* //p' |egrep -v "$BUGGERED"`
+
+<$PLAN9/src/mkdirs
+
blob - /dev/null
blob + 6066543e5fc8a6f313741c82283cf859e284ed19 (mode 644)
--- /dev/null
+++ src/cmd/rm.c
@@ -0,0 +1,104 @@
+#include <u.h>
+#include <libc.h>
+
+#define rmdir p9rmdir
+
+char	errbuf[ERRMAX];
+int	ignerr = 0;
+
+void
+err(char *f)
+{
+	if(!ignerr){
+		errbuf[0] = '\0';
+		errstr(errbuf, sizeof errbuf);
+		fprint(2, "rm: %s: %s\n", f, errbuf);
+	}
+}
+
+/*
+ * f is a non-empty directory. Remove its contents and then it.
+ */
+void
+rmdir(char *f)
+{
+	char *name;
+	int fd, i, j, n, ndir, nname;
+	Dir *dirbuf;
+
+	fd = open(f, OREAD);
+	if(fd < 0){
+		err(f);
+		return;
+	}
+	n = dirreadall(fd, &dirbuf);
+	close(fd);
+	if(n < 0){
+		err("dirreadall");
+		return;
+	}
+
+	nname = strlen(f)+1+STATMAX+1;	/* plenty! */
+	name = malloc(nname);
+	if(name == 0){
+		err("memory allocation");
+		return;
+	}
+
+	ndir = 0;
+	for(i=0; i<n; i++){
+		snprint(name, nname, "%s/%s", f, dirbuf[i].name);
+		if(remove(name) != -1)
+			dirbuf[i].qid.type = QTFILE;	/* so we won't recurse */
+		else{
+			if(dirbuf[i].qid.type & QTDIR)
+				ndir++;
+			else
+				err(name);
+		}
+	}
+	if(ndir)
+		for(j=0; j<n; j++)
+			if(dirbuf[j].qid.type & QTDIR){
+				snprint(name, nname, "%s/%s", f, dirbuf[j].name);
+				rmdir(name);
+			}
+	if(remove(f) == -1)
+		err(f);
+	free(name);
+	free(dirbuf);
+}
+void
+main(int argc, char *argv[])
+{
+	int i;
+	int recurse;
+	char *f;
+	Dir *db;
+
+	ignerr = 0;
+	recurse = 0;
+	ARGBEGIN{
+	case 'r':
+		recurse = 1;
+		break;
+	case 'f':
+		ignerr = 1;
+		break;
+	default:
+		fprint(2, "usage: rm [-fr] file ...\n");
+		exits("usage");
+	}ARGEND
+	for(i=0; i<argc; i++){
+		f = argv[i];
+		if(remove(f) != -1)
+			continue;
+		db = nil;
+		if(recurse && (db=dirstat(f))!=nil && (db->qid.type&QTDIR))
+			rmdir(f);
+		else
+			err(f);
+		free(db);
+	}
+	exits(errbuf);
+}
blob - /dev/null
blob + 062bb8a4a9635ed19807e6ff864c9a3963ac4958 (mode 644)
--- /dev/null
+++ src/cmd/seq.c
@@ -0,0 +1,92 @@
+#include <u.h>
+#include <libc.h>
+
+double	min = 1.0;
+double	max = 0.0;
+double	incr = 1.0;
+int	constant = 0;
+int	nsteps;
+char	*format;
+
+void
+usage(void)
+{
+	fprint(2, "usage: seq [-fformat] [-w] [first [incr]] last\n");
+	exits("usage");
+}
+
+void
+buildfmt(void)
+{
+	int i;
+	char *dp;
+	int w, p, maxw, maxp;
+	static char fmt[16];
+	char buf[32];
+
+	format = "%g\n";
+	if(!constant)
+		return;
+	maxw = 0;
+	maxp = 0;
+	for(i=0; i<=nsteps; i++){
+		sprint(buf, "%g", min+i*incr);
+		if(strchr(buf, 'e')!=0)
+			return;
+		dp = strchr(buf,'.');
+		w = dp==0? strlen(buf): dp-buf;
+		p = dp==0? 0: strlen(strchr(buf,'.')+1);
+		if(w>maxw)
+			maxw = w;
+		if(p>maxp)
+			maxp = p;
+	}
+	if(maxp > 0)
+		maxw += maxp+1;
+	sprint(fmt,"%%%d.%df\n", maxw, maxp);
+	format = fmt;
+}
+
+void
+main(int argc, char *argv[]){
+	int i, j, n;
+	char buf[256], ffmt[4096];
+
+	ARGBEGIN{
+	case 'w':
+		constant++;
+		break;
+	case 'f':
+		format = ARGF();
+		if(format[strlen(format)-1] != '\n'){
+			sprint(ffmt, "%s\n", format);
+			format = ffmt;
+		}
+		break;
+	default:
+		goto out;
+	}ARGEND
+    out:
+	if(argc<1 || argc>3)
+		usage();
+	max = atof(argv[argc-1]);
+	if(argc > 1)
+		min = atof(argv[0]);
+	if(argc > 2)
+		incr = atof(argv[1]);
+	if(incr == 0){
+		fprint(2, "seq: zero increment\n");
+		exits("zero increment");
+	}
+	nsteps = (max-min)/incr+.5;
+	if(!format)
+		buildfmt();
+	for(i=0; i<=nsteps; i++){
+		n = sprint(buf, format, min+i*incr);
+		if(constant)
+			for(j=0; buf[j]==' '; j++)
+				buf[j] ='0';
+		write(1, buf, n);
+	}
+	exits(0);
+}
blob - /dev/null
blob + d8c449faa8802bc8ffac5b62b5e07adecb245ecb (mode 644)
--- /dev/null
+++ src/cmd/sha1sum.c
@@ -0,0 +1,61 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <libsec.h>
+
+static int
+digestfmt(Fmt *fmt)
+{
+	char buf[SHA1dlen*2+1];
+	uchar *p;
+	int i;
+
+	p = va_arg(fmt->args, uchar*);
+	for(i=0; i<SHA1dlen; i++)
+		sprint(buf+2*i, "%.2ux", p[i]);
+	return fmtstrcpy(fmt, buf);
+}
+
+static void
+sum(int fd, char *name)
+{
+	int n;
+	uchar buf[8192], digest[SHA1dlen];
+	DigestState *s;
+
+	s = sha1(nil, 0, nil, nil);
+	while((n = read(fd, buf, sizeof buf)) > 0)
+		sha1(buf, n, nil, s);
+	sha1(nil, 0, digest, s);
+	if(name == nil)
+		print("%M\n", digest);
+	else
+		print("%M\t%s\n", digest, name);
+}
+
+void
+main(int argc, char *argv[])
+{
+	int i, fd;
+
+	ARGBEGIN{
+	default:
+		fprint(2, "usage: sha1sum [file...]\n");
+		exits("usage");
+	}ARGEND
+
+	fmtinstall('M', digestfmt);
+
+	if(argc == 0)
+		sum(0, nil);
+	else for(i = 0; i < argc; i++){
+		fd = open(argv[i], OREAD);
+		if(fd < 0){
+			fprint(2, "sha1sum: can't open %s: %r\n", argv[i]);
+			continue;
+		}
+		sum(fd, argv[i]);
+		close(fd);
+	}
+	exits(nil);
+}
blob - /dev/null
blob + 68cdc7a46d2dcbaf83494f9b2ffb3aa1a06e2b0e (mode 644)
--- /dev/null
+++ src/cmd/sleep.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+void
+main(int argc, char *argv[])
+{
+	long secs;
+
+	if(argc>1)
+		for(secs = atol(argv[1]); secs > 0; secs--)
+			sleep(1000);
+	exits(0);
+}
blob - /dev/null
blob + 359965e89771c34d7b758ce38283a89ab1baa55a (mode 644)
--- /dev/null
+++ src/cmd/sort.c
@@ -0,0 +1,1767 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<bio.h>
+
+/*
+bugs:
+	00/ff for end of file can conflict with 00/ff characters
+*/
+
+enum
+{
+	Nline	= 100000,		/* default max number of lines saved in memory */
+	Nmerge	= 10,			/* max number of temporary files merged */
+	Nfield	= 20,			/* max number of argument fields */
+
+	Bflag	= 1<<0,			/* flags per field */
+	B1flag	= 1<<1,
+
+	Dflag	= 1<<2,
+	Fflag	= 1<<3,
+	Gflag	= 1<<4,
+	Iflag	= 1<<5,
+	Mflag	= 1<<6,
+	Nflag	= 1<<7,
+	Rflag	= 1<<8,
+	Wflag	= 1<<9,
+
+	NSstart	= 0,			/* states for number to key decoding */
+	NSsign,
+	NSzero,
+	NSdigit,
+	NSpoint,
+	NSfract,
+	NSzerofract,
+	NSexp,
+	NSexpsign,
+	NSexpdigit,
+};
+
+typedef	struct	Line	Line;
+typedef	struct	Key	Key;
+typedef	struct	Merge	Merge;
+typedef	struct	Field	Field;
+
+struct	Line
+{
+	Key*	key;
+	int	llen;		/* always >= 1 */
+	uchar	line[1];	/* always ends in '\n' */
+};
+
+struct	Merge
+{
+	Key*	key;		/* copy of line->key so (Line*) looks like (Merge*) */
+	Line*	line;		/* line at the head of a merged temp file */
+	int	fd;		/* file descriptor */
+	Biobuf	b;		/* iobuf for reading a temp file */
+};
+
+struct	Key
+{
+	int	klen;
+	uchar	key[1];
+};
+
+struct	Field
+{
+	int	beg1;
+	int	beg2;
+	int	end1;
+	int	end2;
+
+	long	flags;
+	uchar	mapto[256];
+
+	void	(*dokey)(Key*, uchar*, uchar*, Field*);
+};
+
+struct args
+{
+	char*	ofile;
+	char*	tname;
+	Rune	tabchar;
+	char	cflag;
+	char	uflag;
+	char	vflag;
+	int	nfield;
+	int	nfile;
+	Field	field[Nfield];
+
+	Line**	linep;
+	long	nline;			/* number of lines in this temp file */
+	long	lineno;			/* overall ordinal for -s option */
+	int	ntemp;
+	long	mline;			/* max lines per file */
+} args;
+
+extern	int	latinmap[];
+extern	Rune*	month[12];
+
+void	buildkey(Line*);
+void	doargs(int, char*[]);
+void	dofield(char*, int*, int*, int, int);
+void	dofile(Biobuf*);
+void	dokey_(Key*, uchar*, uchar*, Field*);
+void	dokey_dfi(Key*, uchar*, uchar*, Field*);
+void	dokey_gn(Key*, uchar*, uchar*, Field*);
+void	dokey_m(Key*, uchar*, uchar*, Field*);
+void	dokey_r(Key*, uchar*, uchar*, Field*);
+void	done(char*);
+int	kcmp(Key*, Key*);
+void	makemapd(Field*);
+void	makemapm(Field*);
+void	mergefiles(int, int, Biobuf*);
+void	mergeout(Biobuf*);
+void	newfield(void);
+Line*	newline(Biobuf*);
+void	nomem(void);
+void	notifyf(void*, char*);
+void	printargs(void);
+void	printout(Biobuf*);
+void	setfield(int, int);
+uchar*	skip(uchar*, int, int, int, int);
+void	sort4(void*, ulong);
+char*	tempfile(int);
+void	tempout(void);
+void	lineout(Biobuf*, Line*);
+
+void
+main(int argc, char *argv[])
+{
+	int i, f;
+	char *s;
+	Biobuf bbuf;
+
+	notify(notifyf);	/**/
+	doargs(argc, argv);
+	if(args.vflag)
+		printargs();
+
+	for(i=1; i<argc; i++) {
+		s = argv[i];
+		if(s == 0)
+			continue;
+		if(strcmp(s, "-") == 0) {
+			Binit(&bbuf, 0, OREAD);
+			dofile(&bbuf);
+			Bterm(&bbuf);
+			continue;
+		}
+		f = open(s, OREAD);
+		if(f < 0) {
+			fprint(2, "sort: open %s: %r\n", s);
+			done("open");
+		}
+		Binit(&bbuf, f, OREAD);
+		dofile(&bbuf);
+		Bterm(&bbuf);
+		close(f);
+	}
+	if(args.nfile == 0) {
+		Binit(&bbuf, 0, OREAD);
+		dofile(&bbuf);
+		Bterm(&bbuf);
+	}
+	if(args.cflag)
+		done(0);
+	if(args.vflag)
+		fprint(2, "=========\n");
+
+	f = 1;
+	if(args.ofile) {
+		f = create(args.ofile, OWRITE, 0666);
+		if(f < 0) {
+			fprint(2, "sort: create %s: %r\n", args.ofile);
+			done("create");
+		}
+	}
+
+	Binit(&bbuf, f, OWRITE);
+	if(args.ntemp) {
+		tempout();
+		mergeout(&bbuf);
+	} else {
+		printout(&bbuf);
+	}
+	Bterm(&bbuf);
+	done(0);
+}
+
+void
+dofile(Biobuf *b)
+{
+	Line *l, *ol;
+	int n;
+
+	if(args.cflag) {
+		ol = newline(b);
+		if(ol == 0)
+			return;
+		for(;;) {
+			l = newline(b);
+			if(l == 0)
+				break;
+			n = kcmp(ol->key, l->key);
+			if(n > 0 || (n == 0 && args.uflag)) {
+				fprint(2, "sort: -c file not in sort\n"); /**/
+				done("order");
+			}
+			free(ol->key);
+			free(ol);
+			ol = l;
+		}
+		return;
+	}
+
+	if(args.linep == 0) {
+		args.linep = malloc(args.mline * sizeof(args.linep));
+		if(args.linep == 0)
+			nomem();
+	}
+	for(;;) {
+		l = newline(b);
+		if(l == 0)
+			break;
+		if(args.nline >= args.mline)
+			tempout();
+		args.linep[args.nline] = l;
+		args.nline++;
+		args.lineno++;
+	}
+}
+
+void
+notifyf(void *a, char *s)
+{
+	USED(a);
+	if(strcmp(s, "interrupt") == 0)
+		done(0);
+	if(strcmp(s, "hangup") == 0)
+		done(0);
+	if(strcmp(s, "kill") == 0)
+		done(0);
+	if(strncmp(s, "sys: write on closed pipe", 25) == 0)
+		done(0);
+	fprint(2, "sort: note: %s\n", s);
+	abort();
+}
+
+Line*
+newline(Biobuf *b)
+{
+	Line *l;
+	char *p;
+	int n, c;
+
+	p = Brdline(b, '\n');
+	n = Blinelen(b);
+	if(p == 0) {
+		if(n == 0)
+			return 0;
+		l = 0;
+		for(n=0;;) {
+			if((n & 31) == 0) {
+				l = realloc(l, sizeof(Line) +
+					(n+31)*sizeof(l->line[0]));
+				if(l == 0)
+					nomem();
+			}
+			c = Bgetc(b);
+			if(c < 0) {
+				fprint(2, "sort: newline added\n");
+				c = '\n';
+			}
+			l->line[n++] = c;
+			if(c == '\n')
+				break;
+		}
+		l->llen = n;
+		buildkey(l);
+		return l;
+	}
+	l = malloc(sizeof(Line) +
+		(n-1)*sizeof(l->line[0]));
+	if(l == 0)
+		nomem();
+	l->llen = n;
+	memmove(l->line, p, n);
+	buildkey(l);
+	return l;
+}
+
+void
+lineout(Biobuf *b, Line *l)
+{
+	int n, m;
+
+	n = l->llen;
+	m = Bwrite(b, l->line, n);
+	if(n != m)
+		exits("write");
+}
+
+void
+tempout(void)
+{
+	long n;
+	Line **lp, *l;
+	char *tf;
+	int f;
+	Biobuf tb;
+
+	sort4(args.linep, args.nline);
+	tf = tempfile(args.ntemp);
+	args.ntemp++;
+	f = create(tf, OWRITE, 0666);
+	if(f < 0) {
+		fprint(2, "sort: create %s: %r\n", tf);
+		done("create");
+	}
+
+	Binit(&tb, f, OWRITE);
+	lp = args.linep;
+	for(n=args.nline; n>0; n--) {
+		l = *lp++;
+		lineout(&tb, l);
+		free(l->key);
+		free(l);
+	}
+	args.nline = 0;
+	Bterm(&tb);
+	close(f);
+}
+
+void
+done(char *xs)
+{
+	int i;
+
+	for(i=0; i<args.ntemp; i++)
+		remove(tempfile(i));
+	exits(xs);
+}
+
+void
+nomem(void)
+{
+	fprint(2, "sort: out of memory\n");
+	done("mem");
+}
+
+char*
+tempfile(int n)
+{
+	static char file[100];
+	static uint pid;
+	char *dir;
+
+	dir = "/tmp";
+	if(args.tname)
+		dir = args.tname;
+	if(strlen(dir) >= nelem(file)-20) {
+		fprint(2, "temp file directory name is too long: %s\n", dir);
+		done("tdir");
+	}
+
+	if(pid == 0) {
+		pid = getpid();
+		if(pid == 0) {
+			pid = time(0);
+			if(pid == 0)
+				pid = 1;
+		}
+	}
+
+	sprint(file, "%s/sort.%.4d.%.4d", dir, pid%10000, n);
+	return file;
+}
+
+void
+mergeout(Biobuf *b)
+{
+	int n, i, f;
+	char *tf;
+	Biobuf tb;
+
+	for(i=0; i<args.ntemp; i+=n) {
+		n = args.ntemp - i;
+		if(n > Nmerge) {
+			tf = tempfile(args.ntemp);
+			args.ntemp++;
+			f = create(tf, OWRITE, 0666);
+			if(f < 0) {
+				fprint(2, "sort: create %s: %r\n", tf);
+				done("create");
+			}
+			Binit(&tb, f, OWRITE);
+
+			n = Nmerge;
+			mergefiles(i, n, &tb);
+
+			Bterm(&tb);
+			close(f);
+		} else
+			mergefiles(i, n, b);
+	}
+}
+
+void
+mergefiles(int t, int n, Biobuf *b)
+{
+	Merge *m, *mp, **mmp;
+	Key *ok;
+	Line *l;
+	char *tf;
+	int i, f, nn;
+
+	mmp = malloc(n*sizeof(*mmp));
+	mp = malloc(n*sizeof(*mp));
+	if(mmp == 0 || mp == 0)
+		nomem();
+
+	nn = 0;
+	m = mp;
+	for(i=0; i<n; i++,m++) {
+		tf = tempfile(t+i);
+		f = open(tf, OREAD);
+		if(f < 0) {
+			fprint(2, "sort: reopen %s: %r\n", tf);
+			done("open");
+		}
+		m->fd = f;
+		Binit(&m->b, f, OREAD);
+		mmp[nn] = m;
+
+		l = newline(&m->b);
+		if(l == 0)
+			continue;
+		nn++;
+		m->line = l;
+		m->key = l->key;
+	}
+
+	ok = 0;
+	for(;;) {
+		sort4(mmp, nn);
+		m = *mmp;
+		if(nn == 0)
+			break;
+		for(;;) {
+			l = m->line;
+			if(args.uflag && ok && kcmp(ok, l->key) == 0) {
+				free(l->key);
+				free(l);
+			} else {
+				lineout(b, l);
+				if(ok)
+					free(ok);
+				ok = l->key;
+				free(l);
+			}
+
+			l = newline(&m->b);
+			if(l == 0) {
+				nn--;
+				mmp[0] = mmp[nn];
+				break;
+			}
+			m->line = l;
+			m->key = l->key;
+			if(nn > 1 && kcmp(mmp[0]->key, mmp[1]->key) > 0)
+				break;
+		}
+	}
+	if(ok)
+		free(ok);
+
+	m = mp;
+	for(i=0; i<n; i++,m++) {
+		Bterm(&m->b);
+		close(m->fd);
+	}
+
+	free(mp);
+	free(mmp);
+}
+
+int
+kcmp(Key *ka, Key *kb)
+{
+	int n, m;
+
+	/*
+	 * set n to length of smaller key
+	 */
+	n = ka->klen;
+	m = kb->klen;
+	if(n > m)
+		n = m;
+	return memcmp(ka->key, kb->key, n);
+}
+
+void
+printout(Biobuf *b)
+{
+	long n;
+	Line **lp, *l;
+	Key *ok;
+
+	sort4(args.linep, args.nline);
+	lp = args.linep;
+	ok = 0;
+	for(n=args.nline; n>0; n--) {
+		l = *lp++;
+		if(args.uflag && ok && kcmp(ok, l->key) == 0)
+			continue;
+		lineout(b, l);
+		ok = l->key;
+	}
+}
+
+void
+setfield(int n, int c)
+{
+	Field *f;
+
+	f = &args.field[n];
+	switch(c) {
+	default:
+		fprint(2, "sort: unknown option: field.%C\n", c);
+		done("option");
+	case 'b':	/* skip blanks */
+		f->flags |= Bflag;
+		break;
+	case 'd':	/* directory order */
+		f->flags |= Dflag;
+		break;
+	case 'f':	/* fold case */
+		f->flags |= Fflag;
+		break;
+	case 'g':	/* floating point -n case */
+		f->flags |= Gflag;
+		break;
+	case 'i':	/* ignore non-ascii */
+		f->flags |= Iflag;
+		break;
+	case 'M':	/* month */
+		f->flags |= Mflag;
+		break;
+	case 'n':	/* numbers */
+		f->flags |= Nflag;
+		break;
+	case 'r':	/* reverse */
+		f->flags |= Rflag;
+		break;
+	case 'w':	/* ignore white */
+		f->flags |= Wflag;
+		break;
+	}
+}
+
+void
+dofield(char *s, int *n1, int *n2, int off1, int off2)
+{
+	int c, n;
+
+	c = *s++;
+	if(c >= '0' && c <= '9') {
+		n = 0;
+		while(c >= '0' && c <= '9') {
+			n = n*10 + (c-'0');
+			c = *s++;
+		}
+		n -= off1;	/* posix committee: rot in hell */
+		if(n < 0) {
+			fprint(2, "sort: field offset must be positive\n");
+			done("option");
+		}
+		*n1 = n;
+	}
+	if(c == '.') {
+		c = *s++;
+		if(c >= '0' && c <= '9') {
+			n = 0;
+			while(c >= '0' && c <= '9') {
+				n = n*10 + (c-'0');
+				c = *s++;
+			}
+			n -= off2;
+			if(n < 0) {
+				fprint(2, "sort: character offset must be positive\n");
+				done("option");
+			}
+			*n2 = n;
+		}
+	}
+	while(c != 0) {
+		setfield(args.nfield, c);
+		c = *s++;
+	}
+}
+
+void
+printargs(void)
+{
+	int i, n;
+	Field *f;
+	char *prefix;
+
+	fprint(2, "sort");
+	for(i=0; i<=args.nfield; i++) {
+		f = &args.field[i];
+		prefix = " -";
+		if(i) {
+			n = f->beg1;
+			if(n >= 0)
+				fprint(2, " +%d", n);
+			else
+				fprint(2, " +*");
+			n = f->beg2;
+			if(n >= 0)
+				fprint(2, ".%d", n);
+			else
+				fprint(2, ".*");
+
+			if(f->flags & B1flag)
+				fprint(2, "b");
+
+			n = f->end1;
+			if(n >= 0)
+				fprint(2, " -%d", n);
+			else
+				fprint(2, " -*");
+			n = f->end2;
+			if(n >= 0)
+				fprint(2, ".%d", n);
+			else
+				fprint(2, ".*");
+			prefix = "";
+		}
+		if(f->flags & Bflag)
+			fprint(2, "%sb", prefix);
+		if(f->flags & Dflag)
+			fprint(2, "%sd", prefix);
+		if(f->flags & Fflag)
+			fprint(2, "%sf", prefix);
+		if(f->flags & Gflag)
+			fprint(2, "%sg", prefix);
+		if(f->flags & Iflag)
+			fprint(2, "%si", prefix);
+		if(f->flags & Mflag)
+			fprint(2, "%sM", prefix);
+		if(f->flags & Nflag)
+			fprint(2, "%sn", prefix);
+		if(f->flags & Rflag)
+			fprint(2, "%sr", prefix);
+		if(f->flags & Wflag)
+			fprint(2, "%sw", prefix);
+	}
+	if(args.cflag)
+		fprint(2, " -c");
+	if(args.uflag)
+		fprint(2, " -u");
+	if(args.ofile)
+		fprint(2, " -o %s", args.ofile);
+	if(args.mline != Nline)
+		fprint(2, " -l %ld", args.mline);
+	fprint(2, "\n");
+}
+
+void
+newfield(void)
+{
+	int n;
+	Field *f;
+
+	n = args.nfield + 1;
+	if(n >= Nfield) {
+		fprint(2, "sort: too many fields specified\n");
+		done("option");
+	}
+	args.nfield = n;
+	f = &args.field[n];
+	f->beg1 = -1;
+	f->beg2 = -1;
+	f->end1 = -1;
+	f->end2 = -1;
+}
+
+void
+doargs(int argc, char *argv[])
+{
+	int i, c, hadplus;
+	char *s, *p, *q;
+	Field *f;
+
+	hadplus = 0;
+	args.mline = Nline;
+	for(i=1; i<argc; i++) {
+		s = argv[i];
+		c = *s++;
+		if(c == '-') {
+			c = *s;
+			if(c == 0)		/* forced end of arg marker */
+				break;
+			argv[i] = 0;		/* clobber args processed */
+			if(c == '.' || (c >= '0' && c <= '9')) {
+				if(!hadplus)
+					newfield();
+				f = &args.field[args.nfield];
+				dofield(s, &f->end1, &f->end2, 0, 0);
+				hadplus = 0;
+				continue;
+			}
+
+			while(c = *s++)
+			switch(c) {
+			case '-':	/* end of options */
+				i = argc;
+				continue;
+			case 'T':	/* temp directory */
+				if(*s == 0) {
+					i++;
+					if(i < argc) {
+						args.tname = argv[i];
+						argv[i] = 0;
+					}
+				} else
+					args.tname = s;
+				s = strchr(s, 0);
+				break;
+			case 'o':	/* output file */
+				if(*s == 0) {
+					i++;
+					if(i < argc) {
+						args.ofile = argv[i];
+						argv[i] = 0;
+					}
+				} else
+					args.ofile = s;
+				s = strchr(s, 0);
+				break;
+			case 'k':	/* posix key (what were they thinking?) */
+				p = 0;
+				if(*s == 0) {
+					i++;
+					if(i < argc) {
+						p = argv[i];
+						argv[i] = 0;
+					}
+				} else
+					p = s;
+				s = strchr(s, 0);
+				if(p == 0)
+					break;
+
+				newfield();
+				q = strchr(p, ',');
+				if(q)
+					*q++ = 0;
+				f = &args.field[args.nfield];
+				dofield(p, &f->beg1, &f->beg2, 1, 1);
+				if(f->flags & Bflag) {
+					f->flags |= B1flag;
+					f->flags &= ~Bflag;
+				}
+				if(q) {
+					dofield(q, &f->end1, &f->end2, 1, 0);
+					if(f->end2 <= 0)
+						f->end1++;
+				}
+				hadplus = 0;
+				break;
+			case 't':	/* tab character */
+				if(*s == 0) {
+					i++;
+					if(i < argc) {
+						chartorune(&args.tabchar, argv[i]);
+						argv[i] = 0;
+					}
+				} else
+					s += chartorune(&args.tabchar, s);
+				if(args.tabchar == '\n') {
+					fprint(2, "aw come on, rob\n");
+					done("rob");
+				}
+				break;
+			case 'c':	/* check order */
+				args.cflag = 1;
+				break;
+			case 'u':	/* unique */
+				args.uflag = 1;
+				break;
+			case 'v':	/* debugging noise */
+				args.vflag = 1;
+				break;
+			case 'l':
+				if(*s == 0) {
+					i++;
+					if(i < argc) {
+						args.mline = atol(argv[i]);
+						argv[i] = 0;
+					}
+				} else
+					args.mline = atol(s);
+				s = strchr(s, 0);
+				break;
+
+			case 'M':	/* month */
+			case 'b':	/* skip blanks */
+			case 'd':	/* directory order */
+			case 'f':	/* fold case */
+			case 'g':	/* floating numbers */
+			case 'i':	/* ignore non-ascii */
+			case 'n':	/* numbers */
+			case 'r':	/* reverse */
+			case 'w':	/* ignore white */
+				if(args.nfield > 0)
+					fprint(2, "sort: global field set after -k\n");
+				setfield(0, c);
+				break;
+			case 'm':
+				/* option m silently ignored but required by posix */
+				break;
+			default:
+				fprint(2, "sort: unknown option: -%C\n", c);
+				done("option");
+			}
+			continue;
+		}
+		if(c == '+') {
+			argv[i] = 0;		/* clobber args processed */
+			c = *s;
+			if(c == '.' || (c >= '0' && c <= '9')) {
+				newfield();
+				f = &args.field[args.nfield];
+				dofield(s, &f->beg1, &f->beg2, 0, 0);
+				if(f->flags & Bflag) {
+					f->flags |= B1flag;
+					f->flags &= ~Bflag;
+				}
+				hadplus = 1;
+				continue;
+			}
+			fprint(2, "sort: unknown option: +%C\n", c);
+			done("option");
+		}
+		args.nfile++;
+	}
+
+	for(i=0; i<=args.nfield; i++) {
+		f = &args.field[i];
+
+		/*
+		 * global options apply to fields that
+		 * specify no options
+		 */
+		if(f->flags == 0) {
+			f->flags = args.field[0].flags;
+			if(args.field[0].flags & Bflag)
+				f->flags |= B1flag;
+		}
+
+
+		/*
+		 * build buildkey specification
+		 */
+		switch(f->flags & ~(Bflag|B1flag)) {
+		default:
+			fprint(2, "sort: illegal combination of flags: %lx\n", f->flags);
+			done("option");
+		case 0:
+			f->dokey = dokey_;
+			break;
+		case Rflag:
+			f->dokey = dokey_r;
+			break;
+		case Gflag:
+		case Nflag:
+		case Gflag|Nflag:
+		case Gflag|Rflag:
+		case Nflag|Rflag:
+		case Gflag|Nflag|Rflag:
+			f->dokey = dokey_gn;
+			break;
+		case Mflag:
+		case Mflag|Rflag:
+			f->dokey = dokey_m;
+			makemapm(f);
+			break;
+		case Dflag:
+		case Dflag|Fflag:
+		case Dflag|Fflag|Iflag:
+		case Dflag|Fflag|Iflag|Rflag:
+		case Dflag|Fflag|Iflag|Rflag|Wflag:
+		case Dflag|Fflag|Iflag|Wflag:
+		case Dflag|Fflag|Rflag:
+		case Dflag|Fflag|Rflag|Wflag:
+		case Dflag|Fflag|Wflag:
+		case Dflag|Iflag:
+		case Dflag|Iflag|Rflag:
+		case Dflag|Iflag|Rflag|Wflag:
+		case Dflag|Iflag|Wflag:
+		case Dflag|Rflag:
+		case Dflag|Rflag|Wflag:
+		case Dflag|Wflag:
+		case Fflag:
+		case Fflag|Iflag:
+		case Fflag|Iflag|Rflag:
+		case Fflag|Iflag|Rflag|Wflag:
+		case Fflag|Iflag|Wflag:
+		case Fflag|Rflag:
+		case Fflag|Rflag|Wflag:
+		case Fflag|Wflag:
+		case Iflag:
+		case Iflag|Rflag:
+		case Iflag|Rflag|Wflag:
+		case Iflag|Wflag:
+		case Wflag:
+			f->dokey = dokey_dfi;
+			makemapd(f);
+			break;
+		}
+	}
+
+	/*
+	 * random spot checks
+	 */
+	if(args.nfile > 1 && args.cflag) {
+		fprint(2, "sort: -c can have at most one input file\n");
+		done("option");
+	}
+	return;
+}
+
+uchar*
+skip(uchar *l, int n1, int n2, int bflag, int endfield)
+{
+	int i, c, tc;
+	Rune r;
+
+	if(endfield && n1 < 0)
+		return 0;
+
+	c = *l++;
+	tc = args.tabchar;
+	if(tc) {
+		if(tc < Runeself) {
+			for(i=n1; i>0; i--) {
+				while(c != tc) {
+					if(c == '\n')
+						return 0;
+					c = *l++;
+				}
+				if(!(endfield && i == 1))
+					c = *l++;
+			}
+		} else {
+			l--;
+			l += chartorune(&r, (char*)l);
+			for(i=n1; i>0; i--) {
+				while(r != tc) {
+					if(r == '\n')
+						return 0;
+					l += chartorune(&r, (char*)l);
+				}
+				if(!(endfield && i == 1))
+					l += chartorune(&r, (char*)l);
+			}
+			c = r;
+		}
+	} else {
+		for(i=n1; i>0; i--) {
+			while(c == ' ' || c == '\t')
+				c = *l++;
+			while(c != ' ' && c != '\t') {
+				if(c == '\n')
+					return 0;
+				c = *l++;
+			}
+		}
+	}
+
+	if(bflag)
+		while(c == ' ' || c == '\t')
+			c = *l++;
+
+	l--;
+	for(i=n2; i>0; i--) {
+		c = *l;
+		if(c < Runeself) {
+			if(c == '\n')
+				return 0;
+			l++;
+			continue;
+		}
+		l += chartorune(&r, (char*)l);
+	}
+	return l;
+}
+
+void
+dokey_gn(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+	uchar *kp;
+	int c, cl, dp;
+	int state, nzero, exp, expsign, rflag;
+
+	cl = k->klen + 3;
+	kp = k->key + cl;	/* skip place for sign, exponent[2] */
+
+	nzero = 0;		/* number of trailing zeros */
+	exp = 0;		/* value of the exponent */
+	expsign = 0;		/* sign of the exponent */
+	dp = 0x4040;		/* location of decimal point */
+	rflag = f->flags&Rflag;	/* xor of rflag and - sign */
+	state = NSstart;
+
+	for(;; lp++) {
+		if(lp >= lpe)
+			break;
+		c = *lp;
+
+		if(c == ' ' || c == '\t') {
+			switch(state) {
+			case NSstart:
+			case NSsign:
+				continue;
+			}
+			break;
+		}
+		if(c == '+' || c == '-') {
+			switch(state) {
+			case NSstart:
+				state = NSsign;
+				if(c == '-')
+					rflag = !rflag;
+				continue;
+			case NSexp:
+				state = NSexpsign;
+				if(c == '-')
+					expsign = 1;
+				continue;
+			}
+			break;
+		}
+		if(c == '0') {
+			switch(state) {
+			case NSdigit:
+				if(rflag)
+					c = ~c;
+				*kp++ = c;
+				cl++;
+				nzero++;
+				dp++;
+				state = NSdigit;
+				continue;
+			case NSfract:
+				if(rflag)
+					c = ~c;
+				*kp++ = c;
+				cl++;
+				nzero++;
+				state = NSfract;
+				continue;
+			case NSstart:
+			case NSsign:
+			case NSzero:
+				state = NSzero;
+				continue;
+			case NSzerofract:
+			case NSpoint:
+				dp--;
+				state = NSzerofract;
+				continue;
+			case NSexpsign:
+			case NSexp:
+			case NSexpdigit:
+				exp = exp*10 + (c - '0');
+				state = NSexpdigit;
+				continue;
+			}
+			break;
+		}
+		if(c >= '1' && c <= '9') {
+			switch(state) {
+			case NSzero:
+			case NSstart:
+			case NSsign:
+			case NSdigit:
+				if(rflag)
+					c = ~c;
+				*kp++ = c;
+				cl++;
+				nzero = 0;
+				dp++;
+				state = NSdigit;
+				continue;
+			case NSzerofract:
+			case NSpoint:
+			case NSfract:
+				if(rflag)
+					c = ~c;
+				*kp++ = c;
+				cl++;
+				nzero = 0;
+				state = NSfract;
+				continue;
+			case NSexpsign:
+			case NSexp:
+			case NSexpdigit:
+				exp = exp*10 + (c - '0');
+				state = NSexpdigit;
+				continue;
+			}
+			break;
+		}
+		if(c == '.') {
+			switch(state) {
+			case NSstart:
+			case NSsign:
+				state = NSpoint;
+				continue;
+			case NSzero:
+				state = NSzerofract;
+				continue;
+			case NSdigit:
+				state = NSfract;
+				continue;
+			}
+			break;
+		}
+		if((f->flags & Gflag) && (c == 'e' || c == 'E')) {
+			switch(state) {
+			case NSdigit:
+			case NSfract:
+				state = NSexp;
+				continue;
+			}
+			break;
+		}
+		break;
+	}
+
+	switch(state) {
+	/*
+	 * result is zero
+	 */
+	case NSstart:
+	case NSsign:
+	case NSzero:
+	case NSzerofract:
+	case NSpoint:
+		kp = k->key + k->klen;
+		k->klen += 2;
+		kp[0] = 0x20;	/* between + and - */
+		kp[1] = 0;
+		return;
+	/*
+	 * result has exponent
+	 */
+	case NSexpsign:
+	case NSexp:
+	case NSexpdigit:
+		if(expsign)
+			exp = -exp;
+		dp += exp;
+
+	/*
+	 * result is fixed point number
+	 */
+	case NSdigit:
+	case NSfract:
+		kp -= nzero;
+		cl -= nzero;
+		break;
+	}
+
+	/*
+	 * end of number
+	 */
+	c = 0;
+	if(rflag)
+		c = ~c;
+	*kp = c;
+
+	/*
+	 * sign and exponent
+	 */
+	c = 0x30;
+	if(rflag) {
+		c = 0x10;
+		dp = ~dp;
+	}
+	kp = k->key + k->klen;
+	kp[0] = c;
+	kp[1] = (dp >> 8);
+	kp[2] = dp;
+	k->klen = cl+1;
+}
+
+void
+dokey_m(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+	uchar *kp;
+	Rune r, place[3];
+	int c, cl, pc;
+	int rflag;
+
+	rflag = f->flags&Rflag;
+	pc = 0;
+
+	cl = k->klen;
+	kp = k->key + cl;
+
+	for(;;) {
+		/*
+		 * get the character
+		 */
+		if(lp >= lpe)
+			break;
+		c = *lp;
+		if(c >= Runeself) {
+			lp += chartorune(&r, (char*)lp);
+			c = r;
+		} else
+			lp++;
+
+		if(c < nelem(f->mapto)) {
+			c = f->mapto[c];
+			if(c == 0)
+				continue;
+		}
+		place[pc++] = c;
+		if(pc < 3)
+			continue;
+		for(c=11; c>=0; c--)
+			if(memcmp(month[c], place, sizeof(place)) == 0)
+				break;
+		c += 10;
+		if(rflag)
+			c = ~c;
+		*kp++ = c;
+		cl++;
+		break;
+	}
+
+	c = 0;
+	if(rflag)
+		c = ~c;
+	*kp = c;
+	k->klen = cl+1;
+}
+
+void
+dokey_dfi(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+	uchar *kp;
+	Rune r;
+	int c, cl, n, rflag;
+
+	cl = k->klen;
+	kp = k->key + cl;
+	rflag = f->flags & Rflag;
+
+	for(;;) {
+		/*
+		 * get the character
+		 */
+		if(lp >= lpe)
+			break;
+		c = *lp;
+		if(c >= Runeself) {
+			lp += chartorune(&r, (char*)lp);
+			c = r;
+		} else
+			lp++;
+
+		/*
+		 * do the various mappings.
+		 * the common case is handled
+		 * completely by the table.
+		 */
+		if(c != 0 && c < Runeself) {
+			c = f->mapto[c];
+			if(c) {
+				*kp++ = c;
+				cl++;
+			}
+			continue;
+		}
+
+		/*
+		 * for characters out of range,
+		 * the table does not do Rflag.
+		 * ignore is based on mapto[255]
+		 */
+		if(c != 0 && c < nelem(f->mapto)) {
+			c = f->mapto[c];
+			if(c == 0)
+				continue;
+		} else
+			if(f->mapto[nelem(f->mapto)-1] == 0)
+				continue;
+
+		/*
+		 * put it in the key
+		 */
+		r = c;
+		n = runetochar((char*)kp, &r);
+		kp += n;
+		cl += n;
+		if(rflag)
+			while(n > 0) {
+				kp[-n] = ~kp[-n];
+				n--;
+			}
+	}
+
+	/*
+	 * end of key
+	 */
+	k->klen = cl+1;
+	if(rflag) {
+		*kp = ~0;
+		return;
+	}
+	*kp = 0;
+}
+
+void
+dokey_r(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+	int cl, n;
+	uchar *kp;
+
+	USED(f);
+	n = lpe - lp;
+	if(n < 0)
+		n = 0;
+	cl = k->klen;
+	kp = k->key + cl;
+	k->klen = cl+n+1;
+
+	lpe -= 3;
+	while(lp < lpe) {
+		kp[0] = ~lp[0];
+		kp[1] = ~lp[1];
+		kp[2] = ~lp[2];
+		kp[3] = ~lp[3];
+		kp += 4;
+		lp += 4;
+	}
+
+	lpe += 3;
+	while(lp < lpe)
+		*kp++ = ~*lp++;
+	*kp = ~0;
+}
+
+void
+dokey_(Key *k, uchar *lp, uchar *lpe, Field *f)
+{
+	int n, cl;
+	uchar *kp;
+
+	USED(f);
+	n = lpe - lp;
+	if(n < 0)
+		n = 0;
+	cl = k->klen;
+	kp = k->key + cl;
+	k->klen = cl+n+1;
+	memmove(kp, lp, n);
+	kp[n] = 0;
+}
+
+void
+buildkey(Line *l)
+{
+	Key *k;
+	uchar *lp, *lpe;
+	int ll, kl, cl, i, n;
+	Field *f;
+
+	ll = l->llen - 1;
+	kl = 0;			/* allocated length */
+	cl = 0;			/* current length */
+	k = 0;
+
+	for(i=1; i<=args.nfield; i++) {
+		f = &args.field[i];
+		lp = skip(l->line, f->beg1, f->beg2, f->flags&B1flag, 0);
+		if(lp == 0)
+			lp = l->line + ll;
+		lpe = skip(l->line, f->end1, f->end2, f->flags&Bflag, 1);
+		if(lpe == 0)
+			lpe = l->line + ll;
+		n = (lpe - lp) + 1;
+		if(n <= 0)
+			n = 1;
+		if(cl+(n+4) > kl) {
+			kl = cl+(n+4);
+			k = realloc(k, sizeof(Key) +
+				(kl-1)*sizeof(k->key[0]));
+			if(k == 0)
+				nomem();
+		}
+		k->klen = cl;
+		(*f->dokey)(k, lp, lpe, f);
+		cl = k->klen;
+	}
+
+	/*
+	 * global comparisons
+	 */
+	if(!(args.uflag && cl > 0)) {
+		f = &args.field[0];
+		if(cl+(ll+4) > kl) {
+			kl = cl+(ll+4);
+			k = realloc(k, sizeof(Key) +
+				(kl-1)*sizeof(k->key[0]));
+			if(k == 0)
+				nomem();
+		}
+		k->klen = cl;
+		(*f->dokey)(k, l->line, l->line+ll, f);
+		cl = k->klen;
+	}
+
+	l->key = k;
+	k->klen = cl;
+
+	if(args.vflag) {
+		write(2, l->line, l->llen);
+		for(i=0; i<k->klen; i++) {
+			fprint(2, " %.2x", k->key[i]);
+			if(k->key[i] == 0x00 || k->key[i] == 0xff)
+				fprint(2, "\n");
+		}
+	}
+}
+
+void
+makemapm(Field *f)
+{
+	int i, c;
+
+	for(i=0; i<nelem(f->mapto); i++) {
+		c = 1;
+		if(i == ' ' || i == '\t')
+			c = 0;
+		if(i >= 'a' && i <= 'z')
+			c = i + ('A' - 'a');
+		if(i >= 'A' && i <= 'Z')
+			c = i;
+		f->mapto[i] = c;
+		if(args.vflag) {
+			if((i & 15) == 0)
+				fprint(2, "	");
+			fprint(2, " %.2x", c);
+			if((i & 15) == 15)
+				fprint(2, "\n");
+		}
+	}
+}
+
+void
+makemapd(Field *f)
+{
+	int i, j, c;
+
+	for(i=0; i<nelem(f->mapto); i++) {
+		c = i;
+		if(f->flags & Iflag)
+			if(c < 040 || c > 0176)
+				c = -1;
+		if((f->flags & Wflag) && c >= 0)
+			if(c == ' ' || c == '\t')
+				c = -1;
+		if((f->flags & Dflag) && c >= 0)
+			if(!(c == ' ' || c == '\t' ||
+			    (c >= 'a' && c <= 'z') ||
+			    (c >= 'A' && c <= 'Z') ||
+			    (c >= '0' && c <= '9'))) {
+				for(j=0; latinmap[j]; j+=3)
+					if(c == latinmap[j+0] ||
+					   c == latinmap[j+1])
+						break;
+				if(latinmap[j] == 0)
+					c = -1;
+			}
+		if((f->flags & Fflag) && c >= 0) {
+			if(c >= 'a' && c <= 'z')
+				c += 'A' - 'a';
+			for(j=0; latinmap[j]; j+=3)
+				if(c == latinmap[j+0] ||
+				   c == latinmap[j+1]) {
+					c = latinmap[j+2];
+					break;
+				}
+		}
+		if((f->flags & Rflag) && c >= 0 && i > 0 && i < Runeself)
+			c = ~c & 0xff;
+		if(c < 0)
+			c = 0;
+		f->mapto[i] = c;
+		if(args.vflag) {
+			if((i & 15) == 0)
+				fprint(2, "	");
+			fprint(2, " %.2x", c);
+			if((i & 15) == 15)
+				fprint(2, "\n");
+		}
+	}
+}
+
+int	latinmap[] =
+{
+/*	lcase	ucase	fold	*/
+	0xe0,	0xc0,	0x41,		/*	 L'à',	L'À',	L'A',	 */
+	0xe1,	0xc1,	0x41,		/*	 L'á',	L'Á',	L'A',	 */
+	0xe2,	0xc2,	0x41,		/*	 L'â',	L'Â',	L'A',	 */
+	0xe4,	0xc4,	0x41,		/*	 L'ä',	L'Ä',	L'A',	 */
+	0xe3,	0xc3,	0x41,		/*	 L'ã',	L'Ã',	L'A',	 */
+	0xe5,	0xc5,	0x41,		/*	 L'å',	L'Å',	L'A',	 */
+	0xe8,	0xc8,	0x45,		/*	 L'è',	L'È',	L'E',	 */
+	0xe9,	0xc9,	0x45,		/*	 L'é',	L'É',	L'E',	 */
+	0xea,	0xca,	0x45,		/*	 L'ê',	L'Ê',	L'E',	 */
+	0xeb,	0xcb,	0x45,		/*	 L'ë',	L'Ë',	L'E',	 */
+	0xec,	0xcc,	0x49,		/*	 L'ì',	L'Ì',	L'I',	 */
+	0xed,	0xcd,	0x49,		/*	 L'í',	L'Í',	L'I',	 */
+	0xee,	0xce,	0x49,		/*	 L'î',	L'Î',	L'I',	 */
+	0xef,	0xcf,	0x49,		/*	 L'ï',	L'Ï',	L'I',	 */
+	0xf2,	0xd2,	0x4f,		/*	 L'ò',	L'Ò',	L'O',	 */
+	0xf3,	0xd3,	0x4f,		/*	 L'ó',	L'Ó',	L'O',	 */
+	0xf4,	0xd4,	0x4f,		/*	 L'ô',	L'Ô',	L'O',	 */
+	0xf6,	0xd6,	0x4f,		/*	 L'ö',	L'Ö',	L'O',	 */
+	0xf5,	0xd5,	0x4f,		/*	 L'õ',	L'Õ',	L'O',	 */
+	0xf8,	0xd8,	0x4f,		/*	 L'ø',	L'Ø',	L'O',	 */
+	0xf9,	0xd9,	0x55,		/*	 L'ù',	L'Ù',	L'U',	 */
+	0xfa,	0xda,	0x55,		/*	 L'ú',	L'Ú',	L'U',	 */
+	0xfb,	0xdb,	0x55,		/*	 L'û',	L'Û',	L'U',	 */
+	0xfc,	0xdc,	0x55,		/*	 L'ü',	L'Ü',	L'U',	 */
+	0xe6,	0xc6,	0x41,		/*	 L'æ',	L'Æ',	L'A',	 */
+	0xf0,	0xd0,	0x44,		/*	 L'ð',	L'Ð',	L'D',	 */
+	0xf1,	0xd1,	0x4e,		/*	 L'ñ',	L'Ñ',	L'N',	 */
+	0xfd,	0xdd,	0x59,		/*	 L'ý',	L'Ý',	L'Y',	 */
+	0xe7,	0xc7,	0x43,		/*	 L'ç',	L'Ç',	L'C',	 */
+	0,
+};
+
+Rune LJAN[] = { 'J', 'A', 'N', 0 };
+Rune LFEB[] = { 'F', 'E', 'B', 0 };
+Rune LMAR[] = { 'M', 'A', 'R', 0 };
+Rune LAPR[] = { 'A', 'P', 'R', 0 };
+Rune LMAY[] = { 'M', 'A', 'Y', 0 };
+Rune LJUN[] = { 'J', 'U', 'N', 0 };
+Rune LJUL[] = { 'J', 'U', 'L', 0 };
+Rune LAUG[] = { 'A', 'U', 'G', 0 };
+Rune LSEP[] = { 'S', 'E', 'P', 0 };
+Rune LOCT[] = { 'O', 'C', 'T', 0 };
+Rune LNOV[] = { 'N', 'O', 'V', 0 };
+Rune LDEC[] = { 'D', 'E', 'C', 0 };
+
+Rune*	month[12] =
+{
+	LJAN,
+	LFEB,
+	LMAR,
+	LAPR,
+	LMAY,
+	LJUN,
+	LJUL,
+	LAUG,
+	LSEP,
+	LOCT,
+	LNOV,
+	LDEC,
+};
+
+/************** radix sort ***********/
+
+enum
+{
+	Threshold	= 14,
+};
+
+void	rsort4(Key***, ulong, int);
+void	bsort4(Key***, ulong, int);
+
+void
+sort4(void *a, ulong n)
+{
+	if(n > Threshold)
+		rsort4((Key***)a, n, 0);
+	else
+		bsort4((Key***)a, n, 0);
+}
+
+void
+rsort4(Key ***a, ulong n, int b)
+{
+	Key ***ea, ***t, ***u, **t1, **u1, *k;
+	Key ***part[257];
+	static long count[257];
+	long clist[257+257], *cp, *cp1;
+	int c, lowc, higc;
+
+	/*
+	 * pass 1 over all keys,
+	 * count the number of each key[b].
+	 * find low count and high count.
+	 */
+	lowc = 256;
+	higc = 0;
+	ea = a+n;
+	for(t=a; t<ea; t++) {
+		k = **t;
+		n = k->klen;
+		if(b >= n) {
+			count[256]++;
+			continue;
+		}
+		c = k->key[b];
+		n = count[c]++;
+		if(n == 0) {
+			if(c < lowc)
+				lowc = c;
+			if(c > higc)
+				higc = c;
+		}
+	}
+
+	/*
+	 * pass 2 over all counts,
+	 * put partition pointers in part[c].
+	 * save compacted indexes and counts
+	 * in clist[].
+	 */
+	t = a;
+	n = count[256];
+	clist[0] = n;
+	part[256] = t;
+	t += n;
+
+	cp1 = clist+1;
+	cp = count+lowc;
+	for(c=lowc; c<=higc; c++,cp++) {
+		n = *cp;
+		if(n) {
+			cp1[0] = n;
+			cp1[1] = c;
+			cp1 += 2;
+			part[c] = t;
+			t += n;
+		}
+	}
+	*cp1 = 0;
+
+	/*
+	 * pass 3 over all counts.
+	 * chase lowest pointer in each partition
+	 * around a permutation until it comes
+	 * back and is stored where it started.
+	 * static array, count[], should be
+	 * reduced to zero entries except maybe
+	 * count[256].
+	 */
+	for(cp1=clist+1; cp1[0]; cp1+=2) {
+		c = cp1[1];
+		cp = count+c;
+		while(*cp) {
+			t1 = *part[c];
+			for(;;) {
+				k = *t1;
+				n = 256;
+				if(b < k->klen)
+					n = k->key[b];
+				u = part[n]++;
+				count[n]--;
+				u1 = *u;
+				*u = t1;
+				if(n == c)
+					break;
+				t1 = u1;
+			}
+		}
+	}
+
+	/*
+	 * pass 4 over all partitions.
+	 * call recursively.
+	 */
+	b++;
+	t = a + clist[0];
+	count[256] = 0;
+	for(cp1=clist+1; n=cp1[0]; cp1+=2) {
+		if(n > Threshold)
+			rsort4(t, n, b);
+		else
+		if(n > 1)
+			bsort4(t, n, b);
+		t += n;
+	}
+}
+
+/*
+ * bubble sort to pick up
+ * the pieces.
+ */
+void
+bsort4(Key ***a, ulong n, int b)
+{
+	Key ***i, ***j, ***k, ***l, **t;
+	Key *ka, *kb;
+	int n1, n2;
+
+	l = a+n;
+	j = a;
+
+loop:
+	i = j;
+	j++;
+	if(j >= l)
+		return;
+
+	ka = **i;
+	kb = **j;
+	n1 = ka->klen - b;
+	n2 = kb->klen - b;
+	if(n1 > n2)
+		n1 = n2;
+	if(n1 <= 0)
+		goto loop;
+	n2 = ka->key[b] - kb->key[b];
+	if(n2 == 0)
+		n2 = memcmp(ka->key+b, kb->key+b, n1);
+	if(n2 <= 0)
+		goto loop;
+
+	for(;;) {
+		k = i+1;
+
+		t = *k;
+		*k = *i;
+		*i = t;
+
+		if(i <= a)
+			goto loop;
+
+		i--;
+		ka = **i;
+		kb = *t;
+		n1 = ka->klen - b;
+		n2 = kb->klen - b;
+		if(n1 > n2)
+			n1 = n2;
+		if(n1 <= 0)
+			goto loop;
+		n2 = ka->key[b] - kb->key[b];
+		if(n2 == 0)
+			n2 = memcmp(ka->key+b, kb->key+b, n1);
+		if(n2 <= 0)
+			goto loop;
+	}
+}
blob - /dev/null
blob + c4025e207d14d58ab3970f80a92f8bb7e7e7ed9e (mode 644)
--- /dev/null
+++ src/cmd/split.c
@@ -0,0 +1,189 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <regexp.h>
+
+char	digit[] = "0123456789";
+char	*suffix = "";
+char	*stem = "x";
+char	suff[] = "aa";
+char	name[200];
+Biobuf	bout;
+Biobuf	*output = &bout;
+
+extern int nextfile(void);
+extern int matchfile(Resub*);
+extern void openf(void);
+extern char *fold(char*,int);
+extern void usage(void);
+extern void badexp(void);
+
+void
+main(int argc, char *argv[])
+{
+	Reprog *exp;
+	char *pattern = 0;
+	int n = 1000;
+	char *line;
+	int xflag = 0;
+	int iflag = 0;
+	Biobuf bin;
+	Biobuf *b = &bin;
+	char buf[256];
+
+	ARGBEGIN {
+	case 'l':
+	case 'n':
+		n=atoi(EARGF(usage()));
+		break;
+	case 'e':
+		pattern = strdup(EARGF(usage()));
+		break;
+	case 'f':
+		stem = strdup(EARGF(usage()));
+		break;
+	case 's':
+		suffix = strdup(EARGF(usage()));
+		break;
+	case 'x':
+		xflag++;
+		break;
+	case 'i':
+		iflag++;
+		break;
+	default:
+		usage();
+		break;
+
+	} ARGEND;
+
+	if(argc < 0 || argc > 1)
+		usage();
+
+	if(argc != 0) {
+		b = Bopen(argv[0], OREAD);
+		if(b == nil) {
+			fprint(2, "split: can't open %s: %r\n", argv[0]);
+			exits("open");
+		}
+	} else
+		Binit(b, 0, OREAD);
+
+	if(pattern) {
+		if(!(exp = regcomp(iflag? fold(pattern,strlen(pattern)): pattern)))
+			badexp();
+		while((line=Brdline(b,'\n')) != 0) {
+			Resub match[2];
+			memset(match, 0, sizeof match);
+			line[Blinelen(b)-1] = 0;
+			if(regexec(exp,iflag?fold(line,Blinelen(b)-1):line,match,2)) {
+				if(matchfile(match) && xflag)
+					continue;
+			} else if(output == 0)
+				nextfile();	/* at most once */
+			Bwrite(output, line, Blinelen(b)-1);
+			Bputc(output, '\n');
+		}
+	} else {
+		int linecnt = n;
+
+		while((line=Brdline(b,'\n')) != 0) {
+			if(++linecnt > n) {
+				nextfile();
+				linecnt = 1;
+			}
+			Bwrite(output, line, Blinelen(b));
+		}
+
+		/*
+		 * in case we didn't end with a newline, tack whatever's 
+		 * left onto the last file
+		 */
+		while((n = Bread(b, buf, sizeof(buf))) > 0)
+			Bwrite(output, buf, n);
+	}
+	if(b != nil)
+		Bterm(b);
+	exits(0);
+}
+
+int
+nextfile(void)
+{
+	static int canopen = 1;
+	if(suff[0] > 'z') {
+		if(canopen)
+			fprint(2, "split: file %szz not split\n",stem);
+		canopen = 0;
+	} else {
+		strcpy(name, stem);
+		strcat(name, suff);
+		if(++suff[1] > 'z') 
+			suff[1] = 'a', ++suff[0];
+		openf();
+	}
+	return canopen;
+}
+
+int
+matchfile(Resub *match)
+{
+	if(match[1].s.sp) {
+		int len = match[1].e.ep - match[1].s.sp;
+		strncpy(name, match[1].s.sp, len);
+		strcpy(name+len, suffix);
+		openf();
+		return 1;
+	} 
+	return nextfile();
+}
+
+void
+openf(void)
+{
+	static int fd = 0;
+	Bflush(output);
+	Bterm(output);
+	if(fd > 0)
+		close(fd);
+	fd = create(name,OWRITE,0666);
+	if(fd < 0) {
+		fprint(2, "grep: can't create %s: %r\n", name);
+		exits("create");
+	}
+	Binit(output, fd, OWRITE);
+}
+
+char *
+fold(char *s, int n)
+{
+	static char *fline;
+	static int linesize = 0;
+	char *t;
+
+	if(linesize < n+1){
+		fline = realloc(fline,n+1);
+		linesize = n+1;
+	}
+	for(t=fline; *t++ = tolower(*s++); )
+		continue;
+		/* we assume the 'A'-'Z' only appear as themselves
+		 * in a utf encoding.
+		 */
+	return fline;
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: split [-n num] [-e exp] [-f stem] [-s suff] [-x] [-i] [file]\n");
+	exits("usage");
+}
+
+void
+badexp(void)
+{
+	fprint(2, "split: bad regular expression\n");
+	exits("bad regular expression");
+}
blob - /dev/null
blob + dbf57ff85f1a9c0da284456aa5eca6dc8b38939b (mode 644)
--- /dev/null
+++ src/cmd/strings.c
@@ -0,0 +1,88 @@
+#include	<u.h>
+#include 	<libc.h>
+#include	<bio.h>
+
+Biobuf	*fin;
+Biobuf	fout;
+
+#define	MINSPAN		6		/* Min characters in string */
+
+#define BUFSIZE		70
+
+void stringit(char *);
+int isprint(Rune);
+
+void
+main(int argc, char **argv)
+{
+	int i;
+
+	Binit(&fout, 1, OWRITE);
+	if(argc < 2) {
+		stringit("/fd/0");
+		exits(0);
+	}
+
+	for(i = 1; i < argc; i++) {
+		if(argc > 2)
+			print("%s:\n", argv[i]);
+
+		stringit(argv[i]);
+	}
+
+	exits(0);
+}
+
+void
+stringit(char *str)
+{
+	long posn, start;
+	int cnt = 0;
+	long c;
+
+	Rune buf[BUFSIZE];
+
+	if ((fin = Bopen(str, OREAD)) == 0) {
+		perror("open");
+		return;
+	}
+
+	start = 0;
+	posn = Boffset(fin);
+	while((c = Bgetrune(fin)) >= 0) {
+		if(isprint(c)) {
+			if(start == 0)
+				start = posn;
+			buf[cnt++] = c;
+			if(cnt == BUFSIZE-1) {
+				buf[cnt] = 0;
+				Bprint(&fout, "%8ld: %S ...\n", start, buf);
+				start = 0;
+				cnt = 0;
+			}
+		} else {
+			 if(cnt >= MINSPAN) {
+				buf[cnt] = 0;
+				Bprint(&fout, "%8ld: %S\n", start, buf);
+			}
+			start = 0;
+			cnt = 0;
+		}	
+		posn = Boffset(fin);
+	}
+
+	if(cnt >= MINSPAN){
+		buf[cnt] = 0;
+		Bprint(&fout, "%8ld: %S\n", start, buf);
+	}
+	Bterm(fin);
+}
+
+int
+isprint(Rune r)
+{
+	if ((r >= ' ' && r <0x7f) || r > 0xA0)
+		return 1;
+	else
+		return 0;
+}
blob - /dev/null
blob + de006dbb17a3247318ff59dda55cdbc1f9bc1bfc (mode 644)
--- /dev/null
+++ src/cmd/sum.c
@@ -0,0 +1,215 @@
+#include <u.h>
+#include <libc.h>
+
+typedef ulong	Sumfn(ulong, void*, uvlong);
+extern Sumfn	sumr, sum5, sum32;
+char		*sumfile(char*, Sumfn*);
+
+void
+usage(void)
+{
+	fprint(2, "Usage: %s [-r5] [files]\n", argv0);
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	Sumfn *fn = sum32;
+	char *exitstr=0, *s;
+
+	ARGBEGIN{
+	case 'r':
+		fn = sumr;
+		break;
+	case '5':
+		fn = sum5;
+		break;
+	default:
+		usage();
+		break;
+	}ARGEND
+	if(*argv){
+		while(*argv)
+			if(s = sumfile(*argv++, fn))	/* assign = */
+				exitstr = s;
+	}else
+		exitstr = sumfile(0, fn);
+	exits(exitstr);
+}
+
+char*
+sumfile(char *file, Sumfn *fn)
+{
+	int fd;
+	int n;
+	ulong sum;
+	uvlong fsize;
+	char buf[8*1024];
+
+	if(file){
+		if((fd = open(file, OREAD)) < 0){
+			errstr(buf, sizeof buf);
+			fprint(2, "%s: %s: %s\n", argv0, file, buf);
+			return "can't open";
+		}
+	}else
+		fd = 0;
+	fsize = 0;
+	sum = 0;
+	while((n=read(fd, buf, sizeof buf)) > 0){
+		fsize += n;
+		sum = (*fn)(sum, buf, n);
+	}
+	if(n < 0){
+		errstr(buf, sizeof buf);
+		fprint(2, "%s: %s: read error: %s\n", argv0, file? file:"<stdin>", buf);
+		if(file)
+			close(fd);
+		return "read error";
+	}
+	if(file)
+		close(fd);
+	(*fn)(sum, (char*)0, fsize);
+	if(file)
+		print(" %s", file);
+	print("\n");
+	return 0;
+}
+
+#define	VBSIZE		512		/* system v */
+
+ulong
+sum5(ulong sum, void *buf, uvlong uvn)
+{
+	uchar *s, *send;
+	int n;
+
+	if(buf == 0){
+		sum = ((sum>>16)+sum) & 0xFFFF;
+		print("%.5lud%6lld", sum, (uvn+(VBSIZE-1))/VBSIZE);
+		return 0;
+	}
+	n = uvn;
+	for(s=buf, send=s+n; s<send; s++)
+		sum += 0xffff & *s;
+	return sum;
+}
+
+#define	RBSIZE		1024		/* research */
+
+ulong
+sumr(ulong sum, void *buf, uvlong uvn)
+{
+	uchar *s, *send;
+	int n;
+
+	if(buf == 0){
+		sum &= 0xFFFF;
+		print("%.5lud%6lld", sum, (uvn+(RBSIZE-1))/RBSIZE);
+		return 0;
+	}
+	n = uvn;
+	for(s=buf, send=s+n; s<send; s++)
+		if(sum & 1)
+			sum = 0xffff & ((sum>>1)+*s+0x8000);
+		else
+			sum = 0xffff & ((sum>>1)+*s);
+	return sum;
+}
+
+extern ulong crc_table[256];
+
+ulong
+sum32(ulong lcrc, void *buf, uvlong uvn)
+{
+	uchar *s = buf;
+	ulong crc = lcrc;
+	int n;
+
+	n = uvn;
+	if(buf == 0){
+		char x[4];
+
+		x[0] = (n>>24)^0xCC;	/* encode the length but make n==0 not 0 */
+		x[1] = (n>>16)^0x55;
+		x[2] = (n>>8)^0xCC;
+		x[3] = (n)^0x55;
+		crc = sum32(lcrc, x, 4);
+		print("%.8lux %6lld", crc, uvn);
+		return 0;
+	}
+	while(n-- > 0)
+		crc = crc_table[(crc^*s++)&0xff] ^ (crc>>8);
+	return crc;
+}
+
+/*
+ *	CRC 035556101440
+ */
+ulong crc_table[256] = {
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
blob - /dev/null
blob + 448b3eaf7a62805887c434c972ef7a9b0c8a2bd1 (mode 644)
--- /dev/null
+++ src/cmd/tail.c
@@ -0,0 +1,362 @@
+#include	<u.h>
+#include	<libc.h>
+#include	<ctype.h>
+#include	<bio.h>
+
+/*
+ * tail command, posix plus v10 option -r.
+ * the simple command tail -c, legal in v10, is illegal
+ */
+
+long	count;
+int	anycount;
+int	follow;
+int	file	= 0;
+char*	umsg	= "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
+
+Biobuf	bout;
+enum
+{
+	BEG,
+	END
+} origin = END;
+enum
+{
+	CHARS,
+	LINES
+} units = LINES;
+enum
+{
+	FWD,
+	REV
+} dir = FWD;
+
+extern	void	copy(void);
+extern	void	fatal(char*);
+extern	int	getnumber(char*);
+extern	void	keep(void);
+extern	void	reverse(void);
+extern	void	skip(void);
+extern	void	suffix(char*);
+extern	long	tread(char*, long);
+extern	void	trunc(Dir*, Dir**);
+extern	long	tseek(long, int);
+extern	void	twrite(char*, long);
+extern	void	usage(void);
+
+#define JUMP(o,p) tseek(o,p), copy()
+
+void
+main(int argc, char **argv)
+{
+	int seekable, c;
+
+	Binit(&bout, 1, OWRITE);
+	for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
+		if(getnumber(argv[1])) {
+			suffix(argv[1]);
+			continue;
+		} else
+		if(c == '-')
+			switch(argv[1][1]) {
+			case 'c':
+				units = CHARS;
+			case 'n':
+				if(getnumber(argv[1]+2))
+					continue;
+				else
+				if(argc > 2 && getnumber(argv[2])) {
+					argc--, argv++;
+					continue;
+				} else
+					usage();
+			case 'r':
+				dir = REV;
+				continue;
+			case 'f':
+				follow++;
+				continue;
+			case '-':
+				argc--, argv++;
+			}
+		break;
+	}
+	if(dir==REV && (units==CHARS || follow || origin==BEG))
+		fatal("incompatible options");
+	if(!anycount)
+		count = dir==REV? ~0UL>>1: 10;
+	if(origin==BEG && units==LINES && count>0)
+		count--;
+	if(argc > 2)
+		usage();
+	if(argc > 1 && (file=open(argv[1],0)) < 0)
+		fatal(argv[1]);
+	seekable = seek(file,0L,0) == 0;
+
+	if(!seekable && origin==END)
+		keep();
+	else
+	if(!seekable && origin==BEG)
+		skip();
+	else
+	if(units==CHARS && origin==END)
+		JUMP(-count, 2);
+	else
+	if(units==CHARS && origin==BEG)
+		JUMP(count, 0);
+	else
+	if(units==LINES && origin==END)
+		reverse();
+	else
+	if(units==LINES && origin==BEG)
+		skip();
+	if(follow && seekable)
+		for(;;) {
+			static Dir *sb0, *sb1;
+			trunc(sb1, &sb0);
+			copy();
+			trunc(sb0, &sb1);
+			sleep(5000);
+		}
+	exits(0);
+}
+
+void
+trunc(Dir *old, Dir **new)
+{
+	Dir *d;
+	ulong olength;
+
+	d = dirfstat(file);
+	if(d == nil)
+		return;
+	olength = 0;
+	if(old)
+		olength = old->length;
+	if(d->length < olength)
+		d->length = tseek(0L, 0);
+	free(*new);
+	*new = d;
+}
+
+void
+suffix(char *s)
+{
+	while(*s && strchr("0123456789+-", *s))
+		s++;
+	switch(*s) {
+	case 'b':
+		if((count *= 1024) < 0)
+			fatal("too big");
+	case 'c':
+		units = CHARS;
+	case 'l':
+		s++;
+	}
+	switch(*s) {
+	case 'r':
+		dir = REV;
+		return;
+	case 'f':
+		follow++;
+		return;
+	case 0:
+		return;
+	}
+	usage();
+}
+
+/*
+ * read past head of the file to find tail
+ */
+void
+skip(void)
+{
+	int i;
+	long n;
+	char buf[Bsize];
+	if(units == CHARS) {
+		for( ; count>0; count -=n) {
+			n = count<Bsize? count: Bsize;
+			if(!(n = tread(buf, n)))
+				return;
+		}
+	} else /*units == LINES*/ {
+		n = i = 0;
+		while(count > 0) {
+			if(!(n = tread(buf, Bsize)))
+				return;
+			for(i=0; i<n && count>0; i++)
+				if(buf[i]=='\n')
+					count--;
+		}
+		twrite(buf+i, n-i);
+	}
+	copy();
+}
+
+void
+copy(void)
+{
+	long n;
+	char buf[Bsize];
+	while((n=tread(buf, Bsize)) > 0) {
+		twrite(buf, n);
+		Bflush(&bout);	/* for FWD on pipe; else harmless */
+	}
+}
+
+/*
+ * read whole file, keeping the tail
+ *	complexity is length(file)*length(tail).
+ *	could be linear.
+ */
+void
+keep(void)
+{
+	int len = 0;
+	long bufsiz = 0;
+	char *buf = 0;
+	int j, k, n;
+
+	for(n=1; n;) {
+		if(len+Bsize > bufsiz) {
+			bufsiz += 2*Bsize;
+			if(!(buf = realloc(buf, bufsiz+1)))
+				fatal("out of space");
+		}
+		for(; n && len<bufsiz; len+=n)
+			n = tread(buf+len, bufsiz-len);
+		if(count >= len)
+			continue;
+		if(units == CHARS)
+			j = len - count;
+		else {
+			/* units == LINES */
+			j = buf[len-1]=='\n'? len-1: len;
+			for(k=0; j>0; j--)
+				if(buf[j-1] == '\n')
+					if(++k >= count)
+						break;
+		}
+		memmove(buf, buf+j, len-=j);
+	}
+	if(dir == REV) {
+		if(len>0 && buf[len-1]!='\n')
+			buf[len++] = '\n';
+		for(j=len-1 ; j>0; j--)
+			if(buf[j-1] == '\n') {
+				twrite(buf+j, len-j);
+				if(--count <= 0)
+					return;
+				len = j;
+			}
+	}
+	if(count > 0)
+		twrite(buf, len);
+}
+
+/*
+ * count backward and print tail of file
+ */
+void
+reverse(void)
+{
+	int first;
+	long len = 0;
+	long n = 0;
+	long bufsiz = 0;
+	char *buf = 0;
+	long pos = tseek(0L, 2);
+
+	for(first=1; pos>0 && count>0; first=0) {
+		n = pos>Bsize? Bsize: (int)pos;
+		pos -= n;
+		if(len+n > bufsiz) {
+			bufsiz += 2*Bsize;
+			if(!(buf = realloc(buf, bufsiz+1)))
+				fatal("out of space");
+		}
+		memmove(buf+n, buf, len);
+		len += n;
+		tseek(pos, 0);
+		if(tread(buf, n) != n)
+			fatal("length error");
+		if(first && buf[len-1]!='\n')
+			buf[len++] = '\n';
+		for(n=len-1 ; n>0 && count>0; n--)
+			if(buf[n-1] == '\n') {
+				count--;
+				if(dir == REV)
+					twrite(buf+n, len-n);
+				len = n;
+			}
+	}
+	if(dir == FWD) {
+		tseek(n==0? 0 : pos+n+1, 0);
+		copy();
+	} else
+	if(count > 0)
+		twrite(buf, len);
+}
+
+long
+tseek(long o, int p)
+{
+	o = seek(file, o, p);
+	if(o == -1)
+		fatal("");
+	return o;
+}
+
+long
+tread(char *buf, long n)
+{
+	int r = read(file, buf, n);
+	if(r == -1)
+		fatal("");
+	return r;
+}
+
+void
+twrite(char *s, long n)
+{
+	if(Bwrite(&bout, s, n) != n)
+		fatal("");
+}
+
+int
+getnumber(char *s)
+{
+	if(*s=='-' || *s=='+')
+		s++;
+	if(!isdigit(*s))
+		return 0;
+	if(s[-1] == '+')
+		origin = BEG;
+	if(anycount++)
+		fatal("excess option");
+	count = atol(s);
+
+	/* check range of count */
+	if(count < 0 ||	(int)count != count)
+		fatal("too big");
+	return 1;
+}	
+
+void		
+fatal(char *s)
+{
+	char buf[ERRMAX];
+
+	errstr(buf, sizeof buf);
+	fprint(2, "tail: %s: %s\n", s, buf);
+	exits(s);
+}
+
+void
+usage(void)
+{
+	fprint(2, "%s\n", umsg);
+	exits("usage");
+}
blob - /dev/null
blob + 38418c47a7d085ecec2679a7989f6e9ca0c078ca (mode 644)
--- /dev/null
+++ src/cmd/tar.C
@@ -0,0 +1,640 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#define TBLOCK	512
+#define NBLOCK	40	/* maximum blocksize */
+#define DBLOCK	20	/* default blocksize */
+#define NAMSIZ	100
+union	hblock
+{
+	char	dummy[TBLOCK];
+	struct	header
+	{
+		char	name[NAMSIZ];
+		char	mode[8];
+		char	uid[8];
+		char	gid[8];
+		char	size[12];
+		char	mtime[12];
+		char	chksum[8];
+		char	linkflag;
+		char	linkname[NAMSIZ];
+	} dbuf;
+} dblock, tbuf[NBLOCK];
+
+Dir *stbuf;
+Biobuf bout;
+
+int	rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag;
+int	uflag, gflag;
+int	chksum, recno, first;
+int	nblock = DBLOCK;
+
+void	usage(void);
+void	dorep(char **);
+int	endtar(void);
+void	getdir(void);
+void	passtar(void);
+void	putfile(char*, char *, char *);
+void	doxtract(char **);
+void	dotable(void);
+void	putempty(void);
+void	longt(Dir *);
+int	checkdir(char *, int, Qid*);
+void	tomodes(Dir *);
+int	checksum(void);
+int	checkupdate(char *);
+int	prefix(char *, char *);
+int	readtar(char *);
+int	writetar(char *);
+void	backtar(void);
+void	flushtar(void);
+void	affix(int, char *);
+int	volprompt(void);
+void
+main(int argc, char **argv)
+{
+	char *usefile;
+	char *cp, *ap;
+
+	if (argc < 2)
+		usage();
+
+	Binit(&bout, 1, OWRITE);
+	usefile =  0;
+	argv[argc] = 0;
+	argv++;
+	for (cp = *argv++; *cp; cp++) 
+		switch(*cp) {
+		case 'f':
+			usefile = *argv++;
+			if(!usefile)
+				usage();
+			fflag++;
+			break;
+		case 'u':
+			ap = *argv++;
+			if(!ap)
+				usage();
+			uflag = strtoul(ap, 0, 0);
+			break;
+		case 'g':
+			ap = *argv++;
+			if(!ap)
+				usage();
+			gflag = strtoul(ap, 0, 0);
+			break;
+		case 'c':
+			cflag++;
+			rflag++;
+			break;
+		case 'r':
+			rflag++;
+			break;
+		case 'v':
+			vflag++;
+			break;
+		case 'x':
+			xflag++;
+			break;
+		case 'T':
+			Tflag++;
+			break;
+		case 't':
+			tflag++;
+			break;
+		case 'R':
+			Rflag++;
+			break;
+		case '-':
+			break;
+		default:
+			fprint(2, "tar: %c: unknown option\n", *cp);
+			usage();
+		}
+
+	fmtinstall('M', dirmodefmt);
+
+	if (rflag) {
+		if (!usefile) {
+			if (cflag == 0) {
+				fprint(2, "tar: can only create standard output archives\n");
+				exits("arg error");
+			}
+			mt = dup(1, -1);
+			nblock = 1;
+		}
+		else if ((mt = open(usefile, ORDWR)) < 0) {
+			if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) {
+				fprint(2, "tar: cannot open %s: %r\n", usefile);
+				exits("open");
+			}
+		}
+		dorep(argv);
+	}
+	else if (xflag)  {
+		if (!usefile) {
+			mt = dup(0, -1);
+			nblock = 1;
+		}
+		else if ((mt = open(usefile, OREAD)) < 0) {
+			fprint(2, "tar: cannot open %s: %r\n", usefile);
+			exits("open");
+		}
+		doxtract(argv);
+	}
+	else if (tflag) {
+		if (!usefile) {
+			mt = dup(0, -1);
+			nblock = 1;
+		}
+		else if ((mt = open(usefile, OREAD)) < 0) {
+			fprint(2, "tar: cannot open %s: %r\n", usefile);
+			exits("open");
+		}
+		dotable();
+	}
+	else
+		usage();
+	exits(0);
+}
+
+void
+usage(void)
+{
+	fprint(2, "tar: usage  tar {txrc}[Rvf] [tarfile] file1 file2...\n");
+	exits("usage");
+}
+
+void
+dorep(char **argv)
+{
+	char cwdbuf[2048], *cwd, thisdir[2048];
+	char *cp, *cp2;
+	int cd;
+
+	if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) {
+		fprint(2, "tar: can't find current directory: %r\n");
+		exits("cwd");
+	}
+	cwd = cwdbuf;
+
+	if (!cflag) {
+		getdir();
+		do {
+			passtar();
+			getdir();
+		} while (!endtar());
+	}
+
+	while (*argv) {
+		cp2 = *argv;
+		if (!strcmp(cp2, "-C") && argv[1]) {
+			argv++;
+			if (chdir(*argv) < 0)
+				perror(*argv);
+			cwd = *argv;
+			argv++;
+			continue;
+		}
+		cd = 0;
+		for (cp = *argv; *cp; cp++)
+			if (*cp == '/')
+				cp2 = cp;
+		if (cp2 != *argv) {
+			*cp2 = '\0';
+			chdir(*argv);
+			if(**argv == '/')
+				strncpy(thisdir, *argv, sizeof(thisdir));
+			else
+				snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv);
+			*cp2 = '/';
+			cp2++;
+			cd = 1;
+		} else
+			strncpy(thisdir, cwd, sizeof(thisdir));
+		putfile(thisdir, *argv++, cp2);
+		if(cd && chdir(cwd) < 0) {
+			fprint(2, "tar: can't cd back to %s: %r\n", cwd);
+			exits("cwd");
+		}
+	}
+	putempty();
+	putempty();
+	flushtar();
+}
+
+int
+endtar(void)
+{
+	if (dblock.dbuf.name[0] == '\0') {
+		backtar();
+		return(1);
+	}
+	else
+		return(0);
+}
+
+void
+getdir(void)
+{
+	Dir *sp;
+
+	readtar((char*)&dblock);
+	if (dblock.dbuf.name[0] == '\0')
+		return;
+	if(stbuf == nil){
+		stbuf = malloc(sizeof(Dir));
+		if(stbuf == nil) {
+			fprint(2, "tar: can't malloc: %r\n");
+			exits("malloc");
+		}
+	}
+	sp = stbuf;
+	sp->mode = strtol(dblock.dbuf.mode, 0, 8);
+	sp->uid = "adm";
+	sp->gid = "adm";
+	sp->length = strtol(dblock.dbuf.size, 0, 8);
+	sp->mtime = strtol(dblock.dbuf.mtime, 0, 8);
+	chksum = strtol(dblock.dbuf.chksum, 0, 8);
+	if (chksum != checksum()) {
+		fprint(2, "directory checksum error\n");
+		exits("checksum error");
+	}
+	sp->qid.type = 0;
+	/* the mode test is ugly but sometimes necessary */
+	if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) {
+		sp->qid.type |= QTDIR;
+		sp->mode |= DMDIR;
+	}
+}
+
+void
+passtar(void)
+{
+	long blocks;
+	char buf[TBLOCK];
+
+	if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's')
+		return;
+	blocks = stbuf->length;
+	blocks += TBLOCK-1;
+	blocks /= TBLOCK;
+
+	while (blocks-- > 0)
+		readtar(buf);
+}
+
+void
+putfile(char *dir, char *longname, char *sname)
+{
+	int infile;
+	long blocks;
+	char buf[TBLOCK];
+	char curdir[4096];
+	char shortname[4096];
+	char *cp, *cp2;
+	Dir *db;
+	int i, n;
+
+	if(strlen(sname) > sizeof shortname - 3){
+		fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3);
+		return;
+	}
+	
+	snprint(shortname, sizeof shortname, "./%s", sname);
+	infile = open(shortname, OREAD);
+	if (infile < 0) {
+		fprint(2, "tar: %s: cannot open file - %r\n", longname);
+		return;
+	}
+
+	if(stbuf != nil)
+		free(stbuf);
+	stbuf = dirfstat(infile);
+
+	if (stbuf->qid.type & QTDIR) {
+		/* Directory */
+		for (i = 0, cp = buf; *cp++ = longname[i++];);
+		*--cp = '/';
+		*++cp = 0;
+		if( (cp - buf) >= NAMSIZ) {
+			fprint(2, "tar: %s: file name too long\n", longname);
+			close(infile);
+			return;
+		}
+		stbuf->length = 0;
+		tomodes(stbuf);
+		strcpy(dblock.dbuf.name,buf);
+		dblock.dbuf.linkflag = '5';		/* Directory */
+		sprint(dblock.dbuf.chksum, "%6o", checksum());
+		writetar( (char *) &dblock);
+		if (chdir(shortname) < 0) {
+			fprint(2, "tar: can't cd to %s: %r\n", shortname);
+			snprint(curdir, sizeof(curdir), "cd %s", shortname);
+			exits(curdir);
+		}
+		sprint(curdir, "%s/%s", dir, sname);
+		while ((n = dirread(infile, &db)) > 0) {
+			for(i = 0; i < n; i++){
+				strncpy(cp, db[i].name, sizeof buf - (cp-buf));
+				putfile(curdir, buf, db[i].name);
+			}free(db);
+		}
+		close(infile);
+		if (chdir(dir) < 0 && chdir("..") < 0) {
+			fprint(2, "tar: can't cd to ..(%s): %r\n", dir);
+			snprint(curdir, sizeof(curdir), "cd ..(%s)", dir);
+			exits(curdir);
+		}
+		return;
+	}
+
+
+	tomodes(stbuf);
+
+	cp2 = longname;
+	for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
+	if (i >= NAMSIZ) {
+		fprint(2, "%s: file name too long\n", longname);
+		close(infile);
+		return;
+	}
+
+	blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK;
+	if (vflag) {
+		fprint(2, "a %s ", longname);
+		fprint(2, "%ld blocks\n", blocks);
+	}
+	dblock.dbuf.linkflag = 0;			/* Regular file */
+	sprint(dblock.dbuf.chksum, "%6o", checksum());
+	writetar( (char *) &dblock);
+
+	while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) {
+		writetar(buf);
+		blocks--;
+	}
+	close(infile);
+	if (blocks != 0 || i != 0)
+		fprint(2, "%s: file changed size\n", longname);
+	while (blocks-- >  0)
+		putempty();
+}
+
+
+void
+doxtract(char **argv)
+{
+	Dir null;
+	long blocks, bytes;
+	char buf[TBLOCK], outname[NAMSIZ+4];
+	char **cp;
+	int ofile;
+
+	for (;;) {
+		getdir();
+		if (endtar())
+			break;
+
+		if (*argv == 0)
+			goto gotit;
+
+		for (cp = argv; *cp; cp++)
+			if (prefix(*cp, dblock.dbuf.name))
+				goto gotit;
+		passtar();
+		continue;
+
+gotit:
+		if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid)))
+			continue;
+
+		if (dblock.dbuf.linkflag == '1') {
+			fprint(2, "tar: can't link %s %s\n",
+				dblock.dbuf.linkname, dblock.dbuf.name);
+			remove(dblock.dbuf.name);
+			continue;
+		}
+		if (dblock.dbuf.linkflag == 's') {
+			fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name);
+			continue;
+		}
+		if(dblock.dbuf.name[0] != '/' || Rflag)
+			sprint(outname, "./%s", dblock.dbuf.name);
+		else
+			strcpy(outname, dblock.dbuf.name);
+		if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) {
+			fprint(2, "tar: %s - cannot create: %r\n", outname);
+			passtar();
+			continue;
+		}
+
+		blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK;
+		if (vflag)
+			fprint(2, "x %s, %ld bytes\n",
+				dblock.dbuf.name, bytes);
+		while (blocks-- > 0) {
+			readtar(buf);
+			if (bytes > TBLOCK) {
+				if (write(ofile, buf, TBLOCK) < 0) {
+					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
+					exits("extract write");
+				}
+			} else
+				if (write(ofile, buf, bytes) < 0) {
+					fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
+					exits("extract write");
+				}
+			bytes -= TBLOCK;
+		}
+		if(Tflag){
+			nulldir(&null);
+			null.mtime = stbuf->mtime;
+			dirfwstat(ofile, &null);
+		}
+		close(ofile);
+	}
+}
+
+void
+dotable(void)
+{
+	for (;;) {
+		getdir();
+		if (endtar())
+			break;
+		if (vflag)
+			longt(stbuf);
+		Bprint(&bout, "%s", dblock.dbuf.name);
+		if (dblock.dbuf.linkflag == '1')
+			Bprint(&bout, " linked to %s", dblock.dbuf.linkname);
+		if (dblock.dbuf.linkflag == 's')
+			Bprint(&bout, " -> %s", dblock.dbuf.linkname);
+		Bprint(&bout, "\n");
+		passtar();
+	}
+}
+
+void
+putempty(void)
+{
+	char buf[TBLOCK];
+
+	memset(buf, 0, TBLOCK);
+	writetar(buf);
+}
+
+void
+longt(Dir *st)
+{
+	char *cp;
+
+	Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0);	/* 0/0 uid/gid */
+	Bprint(&bout, "%8lld", st->length);
+	cp = ctime(st->mtime);
+	Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
+}
+
+int
+checkdir(char *name, int mode, Qid *qid)
+{
+	char *cp;
+	int f;
+	Dir *d, null;
+
+	if(Rflag && *name == '/')
+		name++;
+	cp = name;
+	if(*cp == '/')
+		cp++;
+	for (; *cp; cp++) {
+		if (*cp == '/') {
+			*cp = '\0';
+			if (access(name, 0) < 0) {
+				f = create(name, OREAD, DMDIR + 0775L);
+				if(f < 0)
+					fprint(2, "tar: mkdir %s failed: %r\n", name);
+				close(f);
+			}
+			*cp = '/';
+		}
+	}
+
+	/* if this is a directory, chmod it to the mode in the tar plus 700 */
+	if(cp[-1] == '/' || (qid->type&QTDIR)){
+		if((d=dirstat(name)) != 0){
+			nulldir(&null);
+			null.mode = DMDIR | (mode & 0777) | 0700;
+			dirwstat(name, &null);
+			free(d);
+		}
+		return 1;
+	} else
+		return 0;
+}
+
+void
+tomodes(Dir *sp)
+{
+	char *cp;
+
+	for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
+		*cp = '\0';
+	sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777);
+	sprint(dblock.dbuf.uid, "%6o ", uflag);
+	sprint(dblock.dbuf.gid, "%6o ", gflag);
+	sprint(dblock.dbuf.size, "%11llo ", sp->length);
+	sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime);
+}
+
+int
+checksum(void)
+{
+	int i;
+	char *cp;
+
+	for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
+		*cp = ' ';
+	i = 0;
+	for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
+		i += *cp & 0xff;
+	return(i);
+}
+
+int
+prefix(char *s1, char *s2)
+{
+	while (*s1)
+		if (*s1++ != *s2++)
+			return(0);
+	if (*s2)
+		return(*s2 == '/');
+	return(1);
+}
+
+int
+readtar(char *buffer)
+{
+	int i;
+
+	if (recno >= nblock || first == 0) {
+		if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) {
+			fprint(2, "tar: archive read error: %r\n");
+			exits("archive read");
+		}
+		if (first == 0) {
+			if ((i % TBLOCK) != 0) {
+				fprint(2, "tar: archive blocksize error: %r\n");
+				exits("blocksize");
+			}
+			i /= TBLOCK;
+			if (i != nblock) {
+				fprint(2, "tar: blocksize = %d\n", i);
+				nblock = i;
+			}
+		}
+		recno = 0;
+	}
+	first = 1;
+	memmove(buffer, &tbuf[recno++], TBLOCK);
+	return(TBLOCK);
+}
+
+int
+writetar(char *buffer)
+{
+	first = 1;
+	if (recno >= nblock) {
+		if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
+			fprint(2, "tar: archive write error: %r\n");
+			exits("write");
+		}
+		recno = 0;
+	}
+	memmove(&tbuf[recno++], buffer, TBLOCK);
+	if (recno >= nblock) {
+		if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
+			fprint(2, "tar: archive write error: %r\n");
+			exits("write");
+		}
+		recno = 0;
+	}
+	return(TBLOCK);
+}
+
+/*
+ * backup over last tar block
+ */
+void
+backtar(void)
+{
+	seek(mt, -TBLOCK*nblock, 1);
+	recno--;
+}
+
+void
+flushtar(void)
+{
+	write(mt, tbuf, TBLOCK*nblock);
+}
blob - /dev/null
blob + 83db8a08f399dada560e4a6136c023593f056a84 (mode 644)
--- /dev/null
+++ src/cmd/tee.c
@@ -0,0 +1,75 @@
+/*
+ * tee-- pipe fitting
+ */
+
+#include <u.h>
+#include <libc.h>
+
+int	uflag;
+int	aflag;
+int	openf[100];
+
+char in[8192];
+
+int	intignore(void*, char*);
+
+void
+main(int argc, char **argv)
+{
+	int i;
+	int r, n;
+
+	ARGBEGIN {
+	case 'a':
+		aflag++;
+		break;
+
+	case 'i':
+		atnotify(intignore, 1);
+		break;
+
+	case 'u':
+		uflag++;
+		/* uflag is ignored and undocumented; it's a relic from Unix */
+		break;
+
+	default:
+		fprint(2, "usage: tee [-ai] [file ...]\n");
+		exits("usage");
+	} ARGEND
+
+	USED(argc);
+	n = 0;
+	while(*argv) {
+		if(aflag) {
+			openf[n] = open(argv[0], OWRITE);
+			if(openf[n] < 0)
+				openf[n] = create(argv[0], OWRITE, 0666);
+			seek(openf[n], 0L, 2);
+		} else
+			openf[n] = create(argv[0], OWRITE, 0666);
+		if(openf[n] < 0) {
+			fprint(2, "tee: cannot open %s: %r\n", argv[0]);
+		} else
+			n++;
+		argv++;
+	}
+	openf[n++] = 1;
+
+	for(;;) {
+		r = read(0, in, sizeof in);
+		if(r <= 0)
+			exits(nil);
+		for(i=0; i<n; i++)
+			write(openf[i], in, r);
+	}
+}
+
+int
+intignore(void *a, char *msg)
+{
+	USED(a);
+	if(strcmp(msg, "interrupt") == 0)
+		return 1;
+	return 0;
+}
blob - /dev/null
blob + 0282f198fbfd1e548c2d8206f12c10b6cb9ea2fa (mode 644)
--- /dev/null
+++ src/cmd/test.c
@@ -0,0 +1,303 @@
+/*
+ * POSIX standard
+ *	test expression
+ *	[ expression ]
+ *
+ * Plan 9 additions:
+ *	-A file exists and is append-only
+ *	-L file exists and is exclusive-use
+ */
+
+#include <u.h>
+#include <libc.h>
+#define EQ(a,b)	((tmp=a)==0?0:(strcmp(tmp,b)==0))
+
+int	ap;
+int	ac;
+char	**av;
+char	*tmp;
+
+void	synbad(char *, char *);
+int	fsizep(char *);
+int	isdir(char *);
+int	isreg(char *);
+int	isatty(int);
+int	isint(char *, int *);
+int	hasmode(char *, ulong);
+int	tio(char *, int);
+int	e(void), e1(void), e2(void), e3(void);
+
+void
+main(int argc, char *argv[])
+{
+
+	ac = argc; av = argv; ap = 1;
+	if(EQ(argv[0],"[")) {
+		if(!EQ(argv[--ac],"]"))
+			synbad("] missing","");
+	}
+	argv[ac] = 0;
+	if (ac<=1) exits("usage");
+	exits(e()?0:"false");
+}
+
+char *
+nxtarg(int mt)
+{
+	if(ap>=ac){
+		if(mt){
+			ap++;
+			return(0);
+		}
+		synbad("argument expected","");
+	}
+	return(av[ap++]);
+}
+
+int
+nxtintarg(int *pans)
+{
+	if(ap<ac && isint(av[ap], pans)){
+		ap++;
+		return 1;
+	}
+	return 0;
+}
+
+int
+e(void) {
+	int p1;
+
+	p1 = e1();
+	if (EQ(nxtarg(1), "-o")) return(p1 || e());
+	ap--;
+	return(p1);
+}
+
+int
+e1(void) {
+	int p1;
+
+	p1 = e2();
+	if (EQ(nxtarg(1), "-a")) return (p1 && e1());
+	ap--;
+	return(p1);
+}
+
+int
+e2(void) {
+	if (EQ(nxtarg(0), "!"))
+		return(!e2());
+	ap--;
+	return(e3());
+}
+
+int
+e3(void) {
+	int p1;
+	char *a;
+	char *p2;
+	int int1, int2;
+
+	a = nxtarg(0);
+	if(EQ(a, "(")) {
+		p1 = e();
+		if(!EQ(nxtarg(0), ")")) synbad(") expected","");
+		return(p1);
+	}
+
+	if(EQ(a, "-A"))
+		return(hasmode(nxtarg(0), DMAPPEND));
+
+	if(EQ(a, "-L"))
+		return(hasmode(nxtarg(0), DMEXCL));
+
+	if(EQ(a, "-f"))
+		return(isreg(nxtarg(0)));
+
+	if(EQ(a, "-d"))
+		return(isdir(nxtarg(0)));
+
+	if(EQ(a, "-r"))
+		return(tio(nxtarg(0), 4));
+
+	if(EQ(a, "-w"))
+		return(tio(nxtarg(0), 2));
+
+	if(EQ(a, "-x"))
+		return(tio(nxtarg(0), 1));
+
+	if(EQ(a, "-e"))
+		return(tio(nxtarg(0), 0));
+
+	if(EQ(a, "-c"))
+		return(0);
+
+	if(EQ(a, "-b"))
+		return(0);
+
+	if(EQ(a, "-u"))
+		return(0);
+
+	if(EQ(a, "-g"))
+		return(0);
+
+	if(EQ(a, "-s"))
+		return(fsizep(nxtarg(0)));
+
+	if(EQ(a, "-t"))
+		if(ap>=ac || !nxtintarg(&int1))
+			return(isatty(1));
+		else
+			return(isatty(int1));
+
+	if(EQ(a, "-n"))
+		return(!EQ(nxtarg(0), ""));
+	if(EQ(a, "-z"))
+		return(EQ(nxtarg(0), ""));
+
+	p2 = nxtarg(1);
+	if (p2==0)
+		return(!EQ(a,""));
+	if(EQ(p2, "="))
+		return(EQ(nxtarg(0), a));
+
+	if(EQ(p2, "!="))
+		return(!EQ(nxtarg(0), a));
+
+	if(!isint(a, &int1))
+		return(!EQ(a,""));
+
+	if(nxtintarg(&int2)){
+		if(EQ(p2, "-eq"))
+			return(int1==int2);
+		if(EQ(p2, "-ne"))
+			return(int1!=int2);
+		if(EQ(p2, "-gt"))
+			return(int1>int2);
+		if(EQ(p2, "-lt"))
+			return(int1<int2);
+		if(EQ(p2, "-ge"))
+			return(int1>=int2);
+		if(EQ(p2, "-le"))
+			return(int1<=int2);
+	}
+
+	synbad("unknown operator ",p2);
+	return 0;		/* to shut ken up */
+}
+
+int
+tio(char *a, int f)
+{
+	return access (a, f) >= 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localstat(char *f, Dir *dir)
+{
+	Dir *d;
+
+	d = dirstat(f);
+	if(d == 0)
+		return(-1);
+	*dir = *d;
+	dir->name = 0;
+	dir->uid = 0;
+	dir->gid = 0;
+	dir->muid = 0;
+	return 0;
+}
+
+/* copy to local memory; clear names for safety */
+int
+localfstat(int f, Dir *dir)
+{
+	Dir *d;
+
+	d = dirfstat(f);
+	if(d == 0)
+		return(-1);
+	*dir = *d;
+	dir->name = 0;
+	dir->uid = 0;
+	dir->gid = 0;
+	dir->muid = 0;
+	return 0;
+}
+
+int
+hasmode(char *f, ulong m)
+{
+	Dir dir;
+
+	if(localstat(f,&dir)<0)
+		return(0);
+	return(dir.mode&m);
+}
+
+int
+isdir(char *f)
+{
+	Dir dir;
+
+	if(localstat(f,&dir)<0)
+		return(0);
+	return(dir.mode&DMDIR);
+}
+
+int
+isreg(char *f)
+{
+	Dir dir;
+
+	if(localstat(f,&dir)<0)
+		return(0);
+	return(!(dir.mode&DMDIR));
+}
+
+int
+isatty(int fd)
+{
+	Dir d1, d2;
+
+	if(localfstat(fd, &d1) < 0)
+		return 0;
+	if(localstat("/dev/cons", &d2) < 0)
+		return 0;
+	return d1.type==d2.type && d1.dev==d2.dev && d1.qid.path==d2.qid.path;
+}
+
+int
+fsizep(char *f)
+{
+	Dir dir;
+
+	if(localstat(f,&dir)<0)
+		return(0);
+	return(dir.length>0);
+}
+
+void
+synbad(char *s1, char *s2)
+{
+	int len;
+
+	write(2, "test: ", 6);
+	if ((len = strlen(s1)) != 0)
+		write(2, s1, len);
+	if ((len = strlen(s2)) != 0)
+		write(2, s2, len);
+	write(2, "\n", 1);
+	exits("bad syntax");
+}
+
+int
+isint(char *s, int *pans)
+{
+	char *ep;
+
+	*pans = strtol(s, &ep, 0);
+	return (*ep == 0);
+}
blob - /dev/null
blob + d82eaa941589c507a2d86a3c8f42f7d48d49b751 (mode 644)
--- /dev/null
+++ src/cmd/time.c
@@ -0,0 +1,101 @@
+#include <u.h>
+#include <libc.h>
+
+char	output[4096];
+void	add(char*, ...);
+void	error(char*);
+void	notifyf(void*, char*);
+
+void
+main(int argc, char *argv[])
+{
+	int i;
+	Waitmsg *w;
+	long l;
+	char *p;
+	char err[ERRMAX];
+
+	if(argc <= 1){
+		fprint(2, "usage: time command\n");
+		exits("usage");
+	}
+
+	switch(fork()){
+	case -1:
+		error("fork");
+	case 0:
+		exec(argv[1], &argv[1]);
+		if(argv[1][0] != '/' && strncmp(argv[1], "./", 2) &&
+		   strncmp(argv[1], "../", 3)){
+			sprint(output, "/bin/%s", argv[1]);
+			exec(output, &argv[1]);
+		}
+		error(argv[1]);
+	}
+
+	notify(notifyf);
+
+    loop:
+	w = wait();
+	if(w == nil){
+		errstr(err, sizeof err);
+		if(strcmp(err, "interrupted") == 0)
+			goto loop;
+		error("wait");
+	}
+	l = w->time[0];
+	add("%ld.%.2ldu", l/1000, (l%1000)/10);
+	l = w->time[1];
+	add("%ld.%.2lds", l/1000, (l%1000)/10);
+	l = w->time[2];
+	add("%ld.%.2ldr", l/1000, (l%1000)/10);
+	add("\t");
+	for(i=1; i<argc; i++){
+		add("%s", argv[i], 0);
+		if(i>4){
+			add("...");
+			break;
+		}
+	}
+	if(w->msg[0]){
+		p = utfrune(w->msg, ':');
+		if(p && p[1])
+			p++;
+		else
+			p = w->msg;
+		add(" # status=%s", p);
+	}
+	fprint(2, "%s\n", output);
+	exits(w->msg);
+}
+
+void
+add(char *a, ...)
+{
+	static int beenhere=0;
+	va_list arg;
+
+	if(beenhere)
+		strcat(output, " ");
+	va_start(arg, a);
+	vseprint(output+strlen(output), output+sizeof(output), a, arg);
+	va_end(arg);
+	beenhere++;
+}
+
+void
+error(char *s)
+{
+
+	fprint(2, "time: %s: %r\n", s);
+	exits(s);
+}
+
+void
+notifyf(void *a, char *s)
+{
+	USED(a);
+	if(strcmp(s, "interrupt") == 0)
+		noted(NCONT);
+	noted(NDFLT);
+}
blob - /dev/null
blob + 539f89d2cf2f5bc83e8cf77e84a5a16cef0acd2a (mode 644)
--- /dev/null
+++ src/cmd/touch.c
@@ -0,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+
+int touch(int, char *);
+ulong now;
+
+void
+usage(void)
+{
+	fprint(2, "usage: touch [-c] [-t time] files\n");
+	exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+	int nocreate = 0;
+	int status = 0;
+
+	now = time(0);
+	ARGBEGIN{
+	case 't':
+		now = strtoul(EARGF(usage()), 0, 0);
+		break;
+	case 'c':
+		nocreate = 1;
+		break;
+	default:	
+		usage();
+	}ARGEND
+
+	if(!*argv)
+		usage();
+	while(*argv)
+		status += touch(nocreate, *argv++);
+	if(status)
+		exits("touch");
+	exits(0);
+}
+
+int
+touch(int nocreate, char *name)
+{
+	Dir stbuff;
+	int fd;
+
+	nulldir(&stbuff);
+	stbuff.mtime = now;
+	if(dirwstat(name, &stbuff) >= 0)
+		return 0;
+	if(nocreate){
+		fprint(2, "touch: %s: cannot wstat: %r\n", name);
+		return 1;
+	}
+	if ((fd = create(name, OREAD, 0666)) < 0) {
+		fprint(2, "touch: %s: cannot create: %r\n", name);
+		return 1;
+	}
+	dirfwstat(fd, &stbuff);
+	close(fd);
+	return 0;
+}
blob - /dev/null
blob + 39ba747c0b8870a85c3ca588f28a3de7a16a15dc (mode 644)
--- /dev/null
+++ src/cmd/tr.c
@@ -0,0 +1,356 @@
+#include 	<u.h>
+#include 	<libc.h>
+
+typedef struct PCB	/* Control block controlling specification parse */
+{
+	char	*base;		/* start of specification */
+	char	*current;	/* current parse point */
+	long	last;		/* last Rune returned */
+	long	final;		/* final Rune in a span */
+} Pcb;
+
+uchar	bits[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+
+#define	SETBIT(a, c)		((a)[(c)/8] |= bits[(c)&07])
+#define	CLEARBIT(a,c)		((a)[(c)/8] &= ~bits[(c)&07])
+#define	BITSET(a,c)		((a)[(c)/8] & bits[(c)&07])
+
+#define	MAXRUNE	0xFFFF
+
+uchar	f[(MAXRUNE+1)/8];
+uchar	t[(MAXRUNE+1)/8];
+char 	wbuf[4096];
+char	*wptr;
+
+Pcb pfrom, pto;
+
+int cflag;
+int dflag;
+int sflag;
+
+void	complement(void);
+void	delete(void);
+void	squeeze(void);
+void	translit(void);
+void	error(char*);
+long	canon(Pcb*);
+char	*getrune(char*, Rune*);
+void	Pinit(Pcb*, char*);
+void	Prewind(Pcb *p);
+int	readrune(int, long*);
+void	wflush(int);
+void	writerune(int, Rune);
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 's':	sflag++; break;
+	case 'd':	dflag++; break;
+	case 'c':	cflag++; break;
+	default:	error("bad option");
+	}ARGEND
+	if(argc>0)
+		Pinit(&pfrom, argv[0]);
+	if(argc>1)
+		Pinit(&pto, argv[1]);
+	if(argc>2)
+		error("arg count");
+	if(dflag) {
+		if ((sflag && argc != 2) || (!sflag && argc != 1))
+			error("arg count");
+		delete();
+	} else {
+		if (argc != 2)
+			error("arg count");
+		if (cflag)
+			complement();
+		else translit();
+	}
+	exits(0);
+}
+
+void
+delete(void)
+{
+	long c, last;
+
+	if (cflag) {
+		memset((char *) f, 0xff, sizeof f);
+		while ((c = canon(&pfrom)) >= 0)
+			CLEARBIT(f, c);
+	} else {
+		while ((c = canon(&pfrom)) >= 0)
+			SETBIT(f, c);
+	}
+	if (sflag) {
+		while ((c = canon(&pto)) >= 0)
+			SETBIT(t, c);
+	}
+
+	last = 0x10000;
+	while (readrune(0, &c) > 0) {
+		if(!BITSET(f, c) && (c != last || !BITSET(t,c))) {
+			last = c;
+			writerune(1, (Rune) c);
+		}
+	}
+	wflush(1);
+}
+
+void
+complement(void)
+{
+	Rune *p;
+	int i;
+	long from, to, lastc, high;
+
+	lastc = 0;
+	high = 0;
+	while ((from = canon(&pfrom)) >= 0) {
+		if (from > high) high = from;
+		SETBIT(f, from);
+	}
+	while ((to = canon(&pto)) > 0) {
+		if (to > high) high = to;
+		SETBIT(t,to);
+	}
+	Prewind(&pto);
+	if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+		error("can't allocate memory");
+	for (i = 0; i <= high; i++){
+		if (!BITSET(f,i)) {
+			if ((to = canon(&pto)) < 0)
+				to = lastc;
+			else lastc = to;
+			p[i] = to;
+		}
+		else p[i] = i;
+	}
+	if (sflag){
+		lastc = 0x10000;
+		while (readrune(0, &from) > 0) {
+			if (from > high)
+				from = to;
+			else
+				from = p[from];
+			if (from != lastc || !BITSET(t,from)) {
+				lastc = from;
+				writerune(1, (Rune) from);
+			}
+		}
+				
+	} else {
+		while (readrune(0, &from) > 0){
+			if (from > high)
+				from = to;
+			else
+				from = p[from];
+			writerune(1, (Rune) from);
+		}
+	}
+	wflush(1);
+}
+
+void
+translit(void)
+{
+	Rune *p;
+	int i;
+	long from, to, lastc, high;
+
+	lastc = 0;
+	high = 0;
+	while ((from = canon(&pfrom)) >= 0)
+		if (from > high) high = from;
+	Prewind(&pfrom);
+	if ((p = (Rune *) malloc((high+1)*sizeof(Rune))) == 0)
+		error("can't allocate memory");
+	for (i = 0; i <= high; i++)
+		p[i] = i;
+	while ((from = canon(&pfrom)) >= 0) {
+		if ((to = canon(&pto)) < 0)
+			to = lastc;
+		else lastc = to;
+		if (BITSET(f,from) && p[from] != to)
+			error("ambiguous translation");
+		SETBIT(f,from);
+		p[from] = to;
+		SETBIT(t,to);
+	}
+	while ((to = canon(&pto)) >= 0) {
+		SETBIT(t,to);
+	}
+	if (sflag){
+		lastc = 0x10000;
+		while (readrune(0, &from) > 0) {
+			if (from <= high)
+				from = p[from];
+			if (from != lastc || !BITSET(t,from)) {
+				lastc = from;
+				writerune(1, (Rune) from);
+			}
+		}
+				
+	} else {
+		while (readrune(0, &from) > 0) {
+			if (from <= high)
+				from = p[from];
+			writerune(1, (Rune) from);
+		}
+	}
+	wflush(1);
+}
+
+int
+readrune(int fd, long *rp)
+{
+	Rune r;
+	int j;
+	static int i, n;
+	static char buf[4096];
+
+	j = i;
+	for (;;) {
+		if (i >= n) {
+			wflush(1);
+			if (j != i)
+				memcpy(buf, buf+j, n-j);
+			i = n-j;
+			n = read(fd, &buf[i], sizeof(buf)-i);
+			if (n < 0)
+				error("read error");
+			if (n == 0)
+				return 0;
+			j = 0;
+			n += i;
+		}
+		i++;
+		if (fullrune(&buf[j], i-j))
+			break;
+	}
+	chartorune(&r, &buf[j]);
+	*rp = r;
+	return 1;
+}
+
+void
+writerune(int fd, Rune r)
+{
+	char buf[UTFmax];
+	int n;
+
+	if (!wptr)
+		wptr = wbuf;
+	n = runetochar(buf, (Rune*)&r);
+	if (wptr+n >= wbuf+sizeof(wbuf))
+		wflush(fd);
+	memcpy(wptr, buf, n);
+	wptr += n;
+}
+
+void
+wflush(int fd)
+{
+	if (wptr && wptr > wbuf)
+		if (write(fd, wbuf, wptr-wbuf) != wptr-wbuf)
+			error("write error");
+	wptr = wbuf;
+}
+
+char *
+getrune(char *s, Rune *rp)
+{
+	Rune r;
+	char *save;
+	int i, n;
+
+	s += chartorune(rp, s);
+	if((r = *rp) == '\\' && *s){
+		n = 0;
+		if (*s == 'x') {
+			s++;
+			for (i = 0; i < 4; i++) {
+				save = s;
+				s += chartorune(&r, s);
+				if ('0' <= r && r <= '9')
+					n = 16*n + r - '0';
+				else if ('a' <= r && r <= 'f')
+					n = 16*n + r - 'a' + 10;
+				else if ('A' <= r && r <= 'F')
+					n = 16*n + r - 'A' + 10;
+				else {
+					if (i == 0)
+						*rp = 'x';
+					else *rp = n;
+					return save;
+				}
+			}
+		} else {
+			for(i = 0; i < 3; i++) {
+				save = s;
+				s += chartorune(&r, s);
+				if('0' <= r && r <= '7')
+					n = 8*n + r - '0';
+				else {
+					if (i == 0)
+					{
+						*rp = r;
+						return s;
+					}
+					*rp = n;
+					return save;
+				}
+			}
+			if(n > 0377)
+				error("char>0377");
+		}
+		*rp = n;
+	}
+	return s;
+}
+
+long
+canon(Pcb *p)
+{
+	Rune r;
+
+	if (p->final >= 0) {
+		if (p->last < p->final)
+			return ++p->last;
+		p->final = -1;
+	}
+	if (*p->current == '\0')
+		return -1;
+	if(*p->current == '-' && p->last >= 0 && p->current[1]){
+		p->current = getrune(p->current+1, &r);
+		if (r < p->last)
+			error ("Invalid range specification");
+		if (r > p->last) {
+			p->final = r;
+			return ++p->last;
+		}
+	}
+	p->current = getrune(p->current, &r);
+	p->last = r;
+	return p->last;
+}
+
+void
+Pinit(Pcb *p, char *cp)
+{
+	p->current = p->base = cp;
+	p->last = p->final = -1;
+}
+void
+Prewind(Pcb *p)
+{
+	p->current = p->base;
+	p->last = p->final = -1;
+}
+void
+error(char *s)
+{
+	fprint(2, "%s: %s\n", argv0, s);
+	exits(s);
+}
blob - /dev/null
blob + a04472711b6590b8aa95e52960bfdfd447c453b9 (mode 644)
--- /dev/null
+++ src/cmd/unicode.c
@@ -0,0 +1,122 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+char	usage[] = "unicode { [-t] hex hex ... | hexmin-hexmax ... | [-n] char ... }";
+char	hex[] = "0123456789abcdefABCDEF";
+int	numout = 0;
+int	text = 0;
+char	*err;
+Biobuf	bout;
+
+char	*range(char*[]);
+char	*nums(char*[]);
+char	*chars(char*[]);
+
+void
+main(int argc, char *argv[])
+{
+	ARGBEGIN{
+	case 'n':
+		numout = 1;
+		break;
+	case 't':
+		text = 1;
+		break;
+	}ARGEND
+	Binit(&bout, 1, OWRITE);
+	if(argc == 0){
+		fprint(2, "usage: %s\n", usage);
+		exits("usage");
+	}
+	if(!numout && utfrune(argv[0], '-'))
+		exits(range(argv));
+	if(numout || strchr(hex, argv[0][0])==0)
+		exits(nums(argv));
+	exits(chars(argv));
+}
+
+char*
+range(char *argv[])
+{
+	char *q;
+	int min, max;
+	int i;
+
+	while(*argv){
+		q = *argv;
+		if(strchr(hex, q[0]) == 0){
+    err:
+			fprint(2, "unicode: bad range %s\n", *argv);
+			return "bad range";
+		}
+		min = strtoul(q, &q, 16);
+		if(min<0 || min>0xFFFF || *q!='-')
+			goto err;
+		q++;
+		if(strchr(hex, *q) == 0)
+			goto err;
+		max = strtoul(q, &q, 16);
+		if(max<0 || max>0xFFFF || max<min || *q!=0)
+			goto err;
+		i = 0;
+		do{
+			Bprint(&bout, "%.4x %C", min, min);
+			i++;
+			if(min==max || (i&7)==0)
+				Bprint(&bout, "\n");
+			else
+				Bprint(&bout, "\t");
+			min++;
+		}while(min<=max);
+		argv++;
+	}
+	return 0;
+}
+
+char*
+nums(char *argv[])
+{
+	char *q;
+	Rune r;
+	int w;
+
+	while(*argv){
+		q = *argv;
+		while(*q){
+			w = chartorune(&r, q);
+			if(r==0x80 && (q[0]&0xFF)!=0x80){
+				fprint(2, "unicode: invalid utf string %s\n", *argv);
+				return "bad utf";
+			}
+			Bprint(&bout, "%.4x\n", r);
+			q += w;
+		}
+		argv++;
+	}
+	return 0;
+}
+
+char*
+chars(char *argv[])
+{
+	char *q;
+	int m;
+
+	while(*argv){
+		q = *argv;
+		if(strchr(hex, q[0]) == 0){
+    err:
+			fprint(2, "unicode: bad unicode value %s\n", *argv);
+			return "bad char";
+		}
+		m = strtoul(q, &q, 16);
+		if(m<0 || m>0xFFFF || *q!=0)
+			goto err;
+		Bprint(&bout, "%C", m);
+		if(!text)
+			Bprint(&bout, "\n");
+		argv++;
+	}
+	return 0;
+}
blob - /dev/null
blob + 122fb5eda15f445aa687fc94b7181ae81f6c125d (mode 644)
--- /dev/null
+++ src/cmd/uniq.c
@@ -0,0 +1,169 @@
+/*
+ * Deal with duplicated lines in a file
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define	SIZE	8000
+
+int	fields	= 0;
+int	letters	= 0;
+int	linec	= 0;
+char	mode;
+int	uniq;
+char	*b1, *b2;
+long	bsize;
+Biobuf	fin;
+Biobuf	fout;
+
+int	gline(char *buf);
+void	pline(char *buf);
+int	equal(char *b1, char *b2);
+char*	skip(char *s);
+
+void
+main(int argc, char *argv[])
+{
+	int f;
+
+	bsize = SIZE;
+	b1 = malloc(bsize);
+	b2 = malloc(bsize);
+	f = 0;
+	while(argc > 1) {
+		if(*argv[1] == '-') {
+			if(isdigit(argv[1][1]))
+				fields = atoi(&argv[1][1]);
+			else
+				mode = argv[1][1];
+			argc--;
+			argv++;
+			continue;
+		}
+		if(*argv[1] == '+') {
+			letters = atoi(&argv[1][1]);
+			argc--;
+			argv++;
+			continue;
+		}
+		f = open(argv[1], 0);
+		if(f < 0) {
+			fprint(2, "cannot open %s\n", argv[1]);
+			exits("open");
+		}
+		break;
+	}
+	if(argc > 2) {
+		fprint(2, "unexpected argument %s\n", argv[2]);
+		exits("arg");
+	}
+	Binit(&fin, f, OREAD);
+	Binit(&fout, 1, OWRITE);
+
+	if(gline(b1))
+		exits(0);
+	for(;;) {
+		linec++;
+		if(gline(b2)) {
+			pline(b1);
+			exits(0);
+		}
+		if(!equal(b1, b2)) {
+			pline(b1);
+			linec = 0;
+			do {
+				linec++;
+				if(gline(b1)) {
+					pline(b2);
+					exits(0);
+				}
+			} while(equal(b2, b1));
+			pline(b2);
+			linec = 0;
+		}
+	}
+}
+
+int
+gline(char *buf)
+{
+	char *p;
+
+	p = Brdline(&fin, '\n');
+	if(p == 0)
+		return 1;
+	if(fin.rdline >= bsize-1) {
+		fprint(2, "line too long\n");
+		exits("too long");
+	}
+	memmove(buf, p, fin.rdline);
+	buf[fin.rdline-1] = 0;
+	return 0;
+}
+
+void
+pline(char *buf)
+{
+
+	switch(mode) {
+
+	case 'u':
+		if(uniq) {
+			uniq = 0;
+			return;
+		}
+		break;
+
+	case 'd':
+		if(uniq)
+			break;
+		return;
+
+	case 'c':
+		Bprint(&fout, "%4d ", linec);
+	}
+	uniq = 0;
+	Bprint(&fout, "%s\n", buf);
+}
+
+int
+equal(char *b1, char *b2)
+{
+	char c;
+
+	if(fields || letters) {
+		b1 = skip(b1);
+		b2 = skip(b2);
+	}
+	for(;;) {
+		c = *b1++;
+		if(c != *b2++) {
+			if(c == 0 && mode == 's')
+				return 1;
+			return 0;
+		}
+		if(c == 0) {
+			uniq++;
+			return 1;
+		}
+	}
+}
+
+char*
+skip(char *s)
+{
+	int nf, nl;
+
+	nf = nl = 0;
+	while(nf++ < fields) {
+		while(*s == ' ' || *s == '\t')
+			s++;
+		while(!(*s == ' ' || *s == '\t' || *s == 0) ) 
+			s++;
+	}
+	while(nl++ < letters && *s != 0) 
+			s++;
+	return s;
+}
blob - /dev/null
blob + c161731562d7c9f6b7686ee244b52ccfd4b25a2e (mode 644)
--- /dev/null
+++ src/cmd/unutf.c
@@ -0,0 +1,16 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+Biobuf bin;
+
+void
+main(void)
+{
+	int c;
+
+	Binit(&bin, 0, OREAD);
+	while((c = Bgetrune(&bin)) >= 0)
+		print("0x%ux\n", c);
+	exits(0);
+}
blob - /dev/null
blob + 83940880e3b169ed3e7583c75928fbd9db7daefe (mode 644)
--- /dev/null
+++ src/cmd/wc.c
@@ -0,0 +1,309 @@
+/*
+ * wc -- count things in utf-encoded text files
+ * Bugs:
+ *	The only white space characters recognized are ' ', '\t' and '\n', even though
+ *	ISO 10646 has many more blanks scattered through it.
+ *	Should count characters that cannot occur in any rune (hex f0-ff) separately.
+ *	Should count non-canonical runes (e.g. hex c1,80 instead of hex 40).
+ */
+#include <u.h>
+#include <libc.h>
+#define	NBUF	(8*1024)
+uvlong nline, tnline, pline;
+uvlong nword, tnword, pword;
+uvlong nrune, tnrune, prune;
+uvlong nbadr, tnbadr, pbadr;
+uvlong nchar, tnchar, pchar;
+void count(int, char *);
+void report(uvlong, uvlong, uvlong, uvlong, uvlong, char *);
+void
+main(int argc, char *argv[])
+{
+	char *status="";
+	int i, f;
+	ARGBEGIN {
+	case 'l': pline++; break;
+	case 'w': pword++; break;
+	case 'r': prune++; break;
+	case 'b': pbadr++; break;
+	case 'c': pchar++; break;
+	default:
+		fprint(2, "Usage: %s [-lwrbc] [file ...]\n", argv0);
+		exits("usage");
+	} ARGEND
+	if(pline+pword+prune+pbadr+pchar == 0) {
+		pline = 1;
+		pword = 1;
+		pchar = 1;
+	}
+	if(argc==0)
+		count(0, 0);
+	else{
+		for(i=0;i<argc;i++){
+			f=open(argv[i], OREAD);
+			if(f<0){
+				perror(argv[i]);
+				status="can't open";
+			}
+			else{
+				count(f, argv[i]);
+				tnline+=nline;
+				tnword+=nword;
+				tnrune+=nrune;
+				tnbadr+=nbadr;
+				tnchar+=nchar;
+				close(f);
+			}
+		}
+		if(argc>1)
+			report(tnline, tnword, tnrune, tnbadr, tnchar, "total");
+	}
+	exits(status);
+}
+void
+report(uvlong nline, uvlong nword, uvlong nrune, uvlong nbadr, uvlong nchar, char *fname)
+{
+	char line[1024], word[128];
+	line[0] = '\0';
+	if(pline){
+		sprint(word, " %7llud", nline);
+		strcat(line, word);
+	}
+	if(pword){
+		sprint(word, " %7llud", nword);
+		strcat(line, word);
+	}
+	if(prune){
+		sprint(word, " %7llud", nrune);
+		strcat(line, word);
+	}
+	if(pbadr){
+		sprint(word, " %7llud", nbadr);
+		strcat(line, word);
+	}
+	if(pchar){
+		sprint(word, " %7llud", nchar);
+		strcat(line, word);
+	}
+	if(fname){
+		sprint(word, " %s",   fname);
+		strcat(line, word);
+	}
+	print("%s\n", line+1);
+}
+/*
+ * How it works.  Start in statesp.  Each time we read a character,
+ * increment various counts, and do state transitions according to the
+ * following table.  If we're not in statesp or statewd when done, the
+ * file ends with a partial rune.
+ *        |                character
+ *  state |09,20| 0a  |00-7f|80-bf|c0-df|e0-ef|f0-ff
+ * -------+-----+-----+-----+-----+-----+-----+-----
+ * statesp|ASP  |ASPN |AWDW |AWDWX|AC2W |AC3W |AWDWX
+ * statewd|ASP  |ASPN |AWD  |AWDX |AC2  |AC3  |AWDX
+ * statec2|ASPX |ASPNX|AWDX |AWDR |AC2X |AC3X |AWDX
+ * statec3|ASPX |ASPNX|AWDX |AC2R |AC2X |AC3X |AWDX
+ */
+enum{			/* actions */
+	AC2,		/* enter statec2 */
+	AC2R,		/* enter statec2, don't count a rune */
+	AC2W,		/* enter statec2, count a word */
+	AC2X,		/* enter statec2, count a bad rune */
+	AC3,		/* enter statec3 */
+	AC3W,		/* enter statec3, count a word */
+	AC3X,		/* enter statec3, count a bad rune */
+	ASP,		/* enter statesp */
+	ASPN,		/* enter statesp, count a newline */
+	ASPNX,		/* enter statesp, count a newline, count a bad rune */
+	ASPX,		/* enter statesp, count a bad rune */
+	AWD,		/* enter statewd */
+	AWDR,		/* enter statewd, don't count a rune */
+	AWDW,		/* enter statewd, count a word */
+	AWDWX,		/* enter statewd, count a word, count a bad rune */
+	AWDX,		/* enter statewd, count a bad rune */
+};
+uchar statesp[256]={	/* looking for the start of a word */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 00-07 */
+AWDW, ASP,  ASPN, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 08-0f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 10-17 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 18-1f */
+ASP,  AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 20-27 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 28-2f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 30-37 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 38-3f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 40-47 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 48-4f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 50-57 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 58-5f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 60-67 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 68-6f */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 70-77 */
+AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW, AWDW,	/* 78-7f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 80-87 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 88-8f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 90-97 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* 98-9f */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a0-a7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* a8-af */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b0-b7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* b8-bf */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* c0-c7 */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* c8-cf */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* d0-d7 */
+AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W, AC2W,	/* d8-df */
+AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,	/* e0-e7 */
+AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W, AC3W,	/* e8-ef */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f0-f7 */
+AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,AWDWX,/* f8-ff */
+};
+uchar statewd[256]={	/* looking for the next character in a word */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 00-07 */
+AWD,  ASP,  ASPN, AWD,  AWD,  AWD,  AWD,  AWD,	/* 08-0f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 10-17 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 18-1f */
+ASP,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 20-27 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 28-2f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 30-37 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 38-3f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 40-47 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 48-4f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 50-57 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 58-5f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 60-67 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 68-6f */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 70-77 */
+AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,  AWD,	/* 78-7f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 80-87 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 88-8f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 90-97 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 98-9f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* a0-a7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* a8-af */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* b0-b7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* b8-bf */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* c0-c7 */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* c8-cf */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* d0-d7 */
+AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,  AC2,	/* d8-df */
+AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,	/* e0-e7 */
+AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,  AC3,	/* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
+};
+uchar statec2[256]={	/* looking for 10xxxxxx to complete a rune */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 00-07 */
+AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,	/* 08-0f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 10-17 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 18-1f */
+ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 20-27 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 28-2f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 30-37 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 38-3f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 40-47 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 48-4f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 50-57 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 58-5f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 60-67 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 68-6f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 70-77 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 78-7f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 80-87 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 88-8f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 90-97 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* 98-9f */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* a0-a7 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* a8-af */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* b0-b7 */
+AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR, AWDR,	/* b8-bf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c0-c7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c8-cf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d0-d7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d8-df */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e0-e7 */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
+};
+uchar statec3[256]={	/* looking for 10xxxxxx,10xxxxxx to complete a rune */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 00-07 */
+AWDX, ASPX, ASPNX,AWDX, AWDX, AWDX, AWDX, AWDX,	/* 08-0f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 10-17 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 18-1f */
+ASPX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 20-27 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 28-2f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 30-37 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 38-3f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 40-47 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 48-4f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 50-57 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 58-5f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 60-67 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 68-6f */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 70-77 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* 78-7f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 80-87 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 88-8f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 90-97 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* 98-9f */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* a0-a7 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* a8-af */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* b0-b7 */
+AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R, AC2R,	/* b8-bf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c0-c7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* c8-cf */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d0-d7 */
+AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X, AC2X,	/* d8-df */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e0-e7 */
+AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X, AC3X,	/* e8-ef */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f0-f7 */
+AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX, AWDX,	/* f8-ff */
+};
+void
+count(int f, char *name)
+{
+	int n;
+	uchar buf[NBUF];
+	uchar *bufp, *ebuf;
+	uchar *state=statesp;
+
+	nline = 0;
+	nword = 0;
+	nrune = 0;
+	nbadr = 0;
+	nchar = 0;
+
+	for(;;){
+		n=read(f, buf, NBUF);
+		if(n<=0)
+			break;
+		nchar+=n;
+		nrune+=n;	/* might be too large, gets decreased later */
+		bufp=buf;
+		ebuf=buf+n;
+		do{
+			switch(state[*bufp]){
+			case AC2:   state=statec2;                   break;
+			case AC2R:  state=statec2; --nrune;          break;
+			case AC2W:  state=statec2; nword++;          break;
+			case AC2X:  state=statec2;          nbadr++; break;
+			case AC3:   state=statec3;                   break;
+			case AC3W:  state=statec3; nword++;          break;
+			case AC3X:  state=statec3;          nbadr++; break;
+			case ASP:   state=statesp;                   break;
+			case ASPN:  state=statesp; nline++;          break;
+			case ASPNX: state=statesp; nline++; nbadr++; break;
+			case ASPX:  state=statesp;          nbadr++; break;
+			case AWD:   state=statewd;                   break;
+			case AWDR:  state=statewd; --nrune;          break;
+			case AWDW:  state=statewd; nword++;          break;
+			case AWDWX: state=statewd; nword++; nbadr++; break;
+			case AWDX:  state=statewd;          nbadr++; break;
+			}
+		}while(++bufp!=ebuf);
+	}
+	if(state!=statesp && state!=statewd)
+		nbadr++;
+	if(n<0)
+		perror(name);
+	report(nline, nword, nrune, nbadr, nchar, name);
+}
blob - /dev/null
blob + 0d6e1bc3c2bb598cd5e9b0864b2d463310f12195 (mode 644)
--- /dev/null
+++ src/cmd/xd.c
@@ -0,0 +1,355 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+unsigned char	odata[16];
+unsigned char	data[16];
+int		ndata;
+unsigned long	addr;
+int		repeats;
+int		swizzle;
+int		flush;
+int		abase=2;
+int		xd(char *, int);
+void		xprint(char *, long);
+void		initarg(void), swizz(void);
+enum{
+	Narg=10
+};
+typedef struct Arg Arg;
+typedef void fmtfn(char *);
+struct Arg
+{
+	int	ascii;		/* 0==none, 1==ascii */
+	int	loglen;		/* 0==1, 1==2, 2==4, 3==8 */
+	int	base;		/* 0==8, 1==10, 2==16 */
+	fmtfn	*fn;		/* function to call with data */
+	char	*afmt;		/* format to use to print address */
+	char	*fmt;		/* format to use to print data */
+}arg[Narg];
+int	narg;
+
+fmtfn	fmt0, fmt1, fmt2, fmt3, fmtc;
+fmtfn *fmt[4] = {
+	fmt0,
+	fmt1,
+	fmt2,
+	fmt3
+};
+
+char *dfmt[4][3] = {
+	" %.3uo",	" %.3ud",	" %.2ux",
+	" %.6uo",	" %.5ud",	" %.4ux",
+	" %.11luo",	" %.10lud",	" %.8lux",
+	" %.22lluo",	" %.20llud",	" %.16llux",
+};
+
+char *cfmt[3][3] = {
+	"   %c",	"   %c", 	"  %c",
+	" %.3s",	" %.3s",	" %.2s",
+	" %.3uo",	" %.3ud",	" %.2ux",
+};
+
+char *afmt[2][3] = {
+	"%.7luo ",	"%.7lud ",	"%.7lux ",
+	"%7luo ",	"%7lud ",	"%7lux ",
+};
+
+Biobuf	bin;
+Biobuf	bout;
+
+void
+main(int argc, char *argv[])
+{
+	int i, err;
+	Arg *ap;
+
+	Binit(&bout, 1, OWRITE);
+	err = 0;
+	ap = 0;
+	while(argc>1 && argv[1][0]=='-' && argv[1][1]){
+		--argc;
+		argv++;
+		argv[0]++;
+		if(argv[0][0] == 'r'){
+			repeats = 1;
+			if(argv[0][1])
+				goto Usage;
+			continue;
+		}
+		if(argv[0][0] == 's'){
+			swizzle = 1;
+			if(argv[0][1])
+				goto Usage;
+			continue;
+		}
+		if(argv[0][0] == 'u'){
+			flush = 1;
+			if(argv[0][1])
+				goto Usage;
+			continue;
+		}
+		if(argv[0][0] == 'a'){
+			argv[0]++;
+			switch(argv[0][0]){
+			case 'o':
+				abase = 0;
+				break;
+			case 'd':
+				abase = 1;
+				break;
+			case 'x':
+				abase = 2;
+				break;
+			default:
+				goto Usage;
+			}
+			if(argv[0][1])
+				goto Usage;
+			continue;
+		}
+		ap = &arg[narg];
+		initarg();
+		while(argv[0][0]){
+			switch(argv[0][0]){
+			case 'c':
+				ap->ascii = 1;
+				ap->loglen = 0;
+				if(argv[0][1] || argv[0][-1]!='-')
+					goto Usage;
+				break;
+			case 'o':
+				ap->base = 0;
+				break;
+			case 'd':
+				ap->base = 1;
+				break;
+			case 'x':
+				ap->base = 2;
+				break;
+			case 'b':
+			case '1':
+				ap->loglen = 0;
+				break;
+			case 'w':
+			case '2':
+				ap->loglen = 1;
+				break;
+			case 'l':
+			case '4':
+				ap->loglen = 2;
+				break;
+			case 'v':
+			case '8':
+				ap->loglen = 3;
+				break;
+			default:
+			Usage:
+   fprint(2, "usage: xd [-u] [-r] [-s] [-a{odx}] [-c|{b1w2l4v8}{odx}] ... file ...\n");
+				exits("usage");
+			}
+			argv[0]++;
+		}
+		if(ap->ascii)
+			ap->fn = fmtc;
+		else
+			ap->fn = fmt[ap->loglen];
+		ap->fmt = dfmt[ap->loglen][ap->base];
+		ap->afmt = afmt[ap>arg][abase];
+	}
+	if(narg == 0)
+		initarg();
+	if(argc == 1)
+		err = xd(0, 0);
+	else if(argc == 2)
+		err = xd(argv[1], 0);
+	else for(i=1; i<argc; i++)
+		err |= xd(argv[i], 1);
+	exits(err? "error" : 0);
+}
+
+void
+initarg(void)
+{
+	Arg *ap;
+
+	ap = &arg[narg++];
+	if(narg >= Narg){
+		fprint(2, "xd: too many formats (max %d)\n", Narg);
+		exits("usage");
+	}
+	ap->ascii = 0;
+	ap->loglen = 2;
+	ap->base = 2;
+	ap->fn = fmt2;
+	ap->fmt = dfmt[ap->loglen][ap->base];
+	ap->afmt = afmt[narg>1][abase];
+}
+
+int
+xd(char *name, int title)
+{
+	int fd;
+	int i, star;
+	Arg *ap;
+	Biobuf *bp;
+
+	fd = 0;
+	if(name){
+		bp = Bopen(name, OREAD);
+		if(bp == 0){
+			fprint(2, "xd: can't open %s\n", name);
+			return 1;
+		}
+	}else{
+		bp = &bin;
+		Binit(bp, fd, OREAD);
+	}
+	if(title)
+		xprint("%s\n", (long)name);
+	addr = 0;
+	star = 0;
+	while((ndata=Bread(bp, data, 16)) >= 0){
+		if(ndata < 16)
+			for(i=ndata; i<16; i++)
+				data[i] = 0;
+		if(swizzle)
+			swizz();
+		if(ndata==16 && repeats){
+			if(addr>0 && data[0]==odata[0]){
+				for(i=1; i<16; i++)
+					if(data[i] != odata[i])
+						break;
+				if(i == 16){
+					addr += 16;
+					if(star == 0){
+						star++;
+						xprint("*\n", 0);
+					}
+					continue;
+				}
+			}
+			for(i=0; i<16; i++)
+				odata[i] = data[i];
+			star = 0;
+		}
+		for(ap=arg; ap<&arg[narg]; ap++){
+			xprint(ap->afmt, addr);
+			(*ap->fn)(ap->fmt);
+			xprint("\n", 0);
+			if(flush)
+				Bflush(&bout);
+		}
+		addr += ndata;
+		if(ndata<16){
+			xprint(afmt[0][abase], addr);
+			xprint("\n", 0);
+			if(flush)
+				Bflush(&bout);
+			break;
+		}
+	}
+	Bterm(bp);
+	return 0;
+}
+
+void
+swizz(void)
+{
+	uchar *p, *q;
+	int i;
+	uchar swdata[16];
+
+	p = data;
+	q = swdata;
+	for(i=0; i<16; i++)
+		*q++ = *p++;
+	p = data;
+	q = swdata;
+	for(i=0; i<4; i++){
+		p[0] = q[3];
+		p[1] = q[2];
+		p[2] = q[1];
+		p[3] = q[0];
+		p += 4;
+		q += 4;
+	}
+}
+
+void
+fmt0(char *f)
+{
+	int i;
+	for(i=0; i<ndata; i++)
+		xprint(f, data[i]);
+}
+
+void
+fmt1(char *f)
+{
+	int i;
+	for(i=0; i<ndata; i+=sizeof(unsigned short))
+		xprint(f, (data[i]<<8)|data[i+1]);
+}
+
+void
+fmt2(char *f)
+{
+	int i;
+	for(i=0; i<ndata; i+=sizeof(unsigned long))
+		xprint(f, (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3]);
+}
+
+void
+fmt3(char *f)
+{
+	int i;
+	unsigned long long v;
+	for(i=0; i<ndata; i+=sizeof(unsigned long long)){
+		v = (data[i]<<24)|(data[i+1]<<16)|(data[i+2]<<8)|data[i+3];
+		v <<= 32;
+		v |= (data[i+4]<<24)|(data[i+1+4]<<16)|(data[i+2+4]<<8)|data[i+3+4];
+		if(Bprint(&bout, f, v)<0){
+			fprint(2, "xd: i/o error\n");
+			exits("i/o error");
+		}
+	}
+}
+
+void
+fmtc(char *f)
+{
+	int i;
+
+	USED(f);
+	for(i=0; i<ndata; i++)
+		switch(data[i]){
+		case '\t':
+			xprint(cfmt[1][2], (long)"\\t");
+			break;
+		case '\r':
+			xprint(cfmt[1][2], (long)"\\r");
+			break;
+		case '\n':
+			xprint(cfmt[1][2], (long)"\\n");
+			break;
+		case '\b':
+			xprint(cfmt[1][2], (long)"\\b");
+			break;
+		default:
+			if(data[i]>=0x7F || ' '>data[i])
+				xprint(cfmt[2][2], data[i]);
+			else
+				xprint(cfmt[0][2], data[i]);
+			break;
+		}
+}
+
+void
+xprint(char *fmt, long d)
+{
+	if(Bprint(&bout, fmt, d)<0){
+		fprint(2, "xd: i/o error\n");
+		exits("i/o error");
+	}
+}
blob - /dev/null
blob + a9a2b2d7b7f583c498f7782d674a846ddd571458 (mode 644)
--- /dev/null
+++ src/cmd/yacc.c
@@ -0,0 +1,2939 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+
+#define	Bungetrune	Bungetc		/* ok for now. */
+
+/*
+ * all these are 32 bit
+ */
+#define TBITSET		((32+NTERMS)/32)	/* BOTCH?? +31 */
+#define BIT(a,i)	((a)[(i)>>5] & (1<<((i)&037)))
+#define SETBIT(a,i)	((a)[(i)>>5] |= (1<<((i)&037)))
+#define NWORDS(n)	(((n)+32)/32)
+
+#define PARSER		"/sys/lib/yaccpar"
+#define PARSERS		"/sys/lib/yaccpars"
+#define TEMPNAME	"y.tmp.XXXXXX"
+#define ACTNAME		"y.acts.XXXXXX"
+#define OFILE		"tab.c"
+#define FILEU		"output"
+#define FILED		"tab.h"
+#define FILEDEBUG	"debug"
+
+enum
+{
+/*
+ * the following are adjustable
+ * according to memory size
+ */
+	ACTSIZE		= 40000,
+	MEMSIZE		= 40000,
+	NSTATES		= 2000,
+	NTERMS		= 511,
+	NPROD		= 1600,
+	NNONTERM	= 600,
+	TEMPSIZE	= 2000,
+	CNAMSZ		= 10000,
+	LSETSIZE	= 2400,
+	WSETSIZE	= 350,
+
+	NAMESIZE	= 50,
+	NTYPES		= 63,
+	ISIZE		= 400,
+
+	PRIVATE		= 0xE000,	/* unicode private use */
+
+	/* relationships which must hold:
+		TBITSET ints must hold NTERMS+1 bits...
+		WSETSIZE >= NNONTERM
+		LSETSIZE >= NNONTERM
+		TEMPSIZE >= NTERMS + NNONTERM + 1
+		TEMPSIZE >= NSTATES
+	*/
+
+	NTBASE		= 010000,
+	ERRCODE		= 8190,
+	ACCEPTCODE	= 8191,
+
+	NOASC		= 0,	/* no assoc. */
+	LASC		= 1,	/* left assoc. */
+	RASC		= 2,	/* right assoc. */
+	BASC		= 3,	/* binary assoc. */
+
+	/* flags for state generation */
+
+	DONE		= 0,
+	MUSTDO		= 1,
+	MUSTLOOKAHEAD	= 2,
+
+	/* flags for a rule having an action, and being reduced */
+
+	ACTFLAG		= 04,
+	REDFLAG		= 010,
+
+	/* output parser flags */
+	YYFLAG1		= -1000,
+
+	/* parse tokens */
+	IDENTIFIER	= PRIVATE,
+	MARK,
+	TERM,
+	LEFT,
+	RIGHT,
+	BINARY,
+	PREC,
+	LCURLY,
+	IDENTCOLON,
+	NUMBER,
+	START,
+	TYPEDEF,
+	TYPENAME,
+	UNION,
+
+	ENDFILE		= 0,
+
+	EMPTY		= 1,
+	WHOKNOWS	= 0,
+	OK		= 1,
+	NOMORE		= -1000,
+};
+
+	/* macros for getting associativity and precedence levels */
+
+#define ASSOC(i)	((i)&03)
+#define PLEVEL(i)	(((i)>>4)&077)
+#define TYPE(i)		(((i)>>10)&077)
+
+	/* macros for setting associativity and precedence levels */
+
+#define SETASC(i,j)	i |= j
+#define SETPLEV(i,j)	i |= (j<<4)
+#define SETTYPE(i,j)	i |= (j<<10)
+
+	/* looping macros */
+
+#define TLOOP(i)	for(i=1; i<=ntokens; i++)
+#define NTLOOP(i)	for(i=0; i<=nnonter; i++)
+#define PLOOP(s,i)	for(i=s; i<nprod; i++)
+#define SLOOP(i)	for(i=0; i<nstate; i++)
+#define WSBUMP(x)	x++
+#define WSLOOP(s,j)	for(j=s; j<cwp; j++)
+#define ITMLOOP(i,p,q)	for(q=pstate[i+1], p=pstate[i]; p<q; p++)
+#define SETLOOP(i)	for(i=0; i<tbitset; i++)
+
+	/* command to clobber tempfiles after use */
+
+#define	ZAPFILE(x)	if(x) remove(x)
+
+	/* I/O descriptors */
+
+Biobuf*	faction;	/* file for saving actions */
+Biobuf*	fdefine;	/* file for #defines */
+Biobuf*	fdebug;		/* y.debug for strings for debugging */
+Biobuf*	ftable;		/* y.tab.c file */
+Biobuf*	ftemp;		/* tempfile to pass 2 */
+Biobuf*	finput;		/* input file */
+Biobuf*	foutput;	/* y.output file */
+
+	/* communication variables between various I/O routines */
+
+char*	infile;			/* input file name */
+int	numbval;		/* value of an input number */
+char	tokname[NAMESIZE+4];	/* input token name, slop for runes and 0 */
+
+	/* structure declarations */
+
+typedef
+struct
+{
+	int	lset[TBITSET];
+} Lkset;
+
+typedef
+struct
+{
+	int*	pitem;
+	Lkset*	look;
+} Item;
+
+typedef
+struct
+{
+	char*	name;
+	int	value;
+} Symb;
+
+typedef
+struct
+{
+	int*	pitem;
+	int	flag;
+	Lkset	ws;
+} Wset;
+
+	/* storage of names */
+
+char	cnames[CNAMSZ];		/* place where token and nonterminal names are stored */
+int	cnamsz = CNAMSZ;	/* size of cnames */
+char*	cnamp = cnames;		/* place where next name is to be put in */
+int	ndefout = 4;		/* number of defined symbols output */
+char*	tempname;
+char*	actname;
+char	ttempname[] = TEMPNAME;
+char	tactname[] = ACTNAME;
+char*	parser = PARSER;
+char*	yydebug;
+
+	/* storage of types */
+int	ntypes;			/* number of types defined */
+char*	typeset[NTYPES];	/* pointers to type tags */
+
+	/* token information */
+
+int	ntokens = 0 ;		/* number of tokens */
+Symb	tokset[NTERMS];
+int	toklev[NTERMS];		/* vector with the precedence of the terminals */
+
+	/* nonterminal information */
+
+int	nnonter = -1;		/* the number of nonterminals */
+Symb	nontrst[NNONTERM];
+int	start;			/* start symbol */
+
+	/* assigned token type values */
+int	extval = 0;
+
+char*	ytabc = OFILE;	/* name of y.tab.c */
+
+	/* grammar rule information */
+
+int	mem0[MEMSIZE] ;		/* production storage */
+int*	mem = mem0;
+int	nprod = 1;		/* number of productions */
+int*	prdptr[NPROD];		/* pointers to descriptions of productions */
+int	levprd[NPROD];		/* precedence levels for the productions */
+int	rlines[NPROD];		/* line number for this rule */
+
+	/* state information */
+
+int	nstate = 0;		/* number of states */
+Item*	pstate[NSTATES+2];	/* pointers to the descriptions of the states */
+int	tystate[NSTATES];	/* contains type information about the states */
+int	defact[NSTATES];	/* the default actions of states */
+int	tstates[NTERMS];	/* states generated by terminal gotos */
+int	ntstates[NNONTERM]; 	/* states generated by nonterminal gotos */
+int	mstates[NSTATES];	/* chain of overflows of term/nonterm generation lists  */
+int	lastred; 		/* the number of the last reduction of a state */
+
+	/* lookahead set information */
+
+Lkset	lkst[LSETSIZE];
+int	nolook;			/* flag to turn off lookahead computations */
+int	tbitset;		/* size of lookahead sets */
+int	nlset = 0;		/* next lookahead set index */
+int	nolook = 0;		/* flag to suppress lookahead computations */
+Lkset	clset;  		/* temporary storage for lookahead computations */
+
+	/* working set information */
+
+Wset	wsets[WSETSIZE];
+Wset*	cwp;
+
+	/* storage for action table */
+
+int	amem[ACTSIZE];		/* action table storage */
+int*	memp = amem;		/* next free action table position */
+int	indgo[NSTATES];		/* index to the stored goto table */
+
+	/* temporary vector, indexable by states, terms, or ntokens */
+
+int	temp1[TEMPSIZE];	/* temporary storage, indexed by terms + ntokens or states */
+int	lineno = 1;		/* current input line number */
+int	fatfl = 1;  		/* if on, error is fatal */
+int	nerrors = 0;		/* number of errors */
+
+	/* statistics collection variables */
+
+int	zzgoent;
+int	zzgobest;
+int	zzacent;
+int	zzexcp;
+int	zzclose;
+int	zzrrconf;
+int	zzsrconf;
+
+int*	ggreed = lkst[0].lset;
+int*	pgo = wsets[0].ws.lset;
+int*	yypgo = &nontrst[0].value;
+
+int	maxspr = 0;  		/* maximum spread of any entry */
+int	maxoff = 0;  		/* maximum offset into a array */
+int*	pmem = mem0;
+int*	maxa;
+int	nxdb = 0;
+int	adb = 0;
+
+
+	/* storage for information about the nonterminals */
+
+int**	pres[NNONTERM+2];  	/* vector of pointers to productions yielding each nonterminal */
+Lkset*	pfirst[NNONTERM+2];	/* vector of pointers to first sets for each nonterminal */
+int	pempty[NNONTERM+1];	/* vector of nonterminals nontrivially deriving e */
+
+	/* random stuff picked out from between functions */
+
+int	indebug = 0;
+Wset*	zzcwp = wsets;
+int	zzgoent = 0;
+int	zzgobest = 0;
+int	zzacent = 0;
+int	zzexcp = 0;
+int	zzclose = 0;
+int	zzsrconf = 0;
+int*	zzmemsz = mem0;
+int	zzrrconf = 0;
+int	pidebug = 0;		/* debugging flag for putitem */
+int	gsdebug = 0;
+int	cldebug = 0;		/* debugging flag for closure */
+int	pkdebug = 0;
+int	g2debug = 0;
+
+struct
+{
+	char*	name;
+	long	value;
+} resrv[] =
+{
+	"binary",	BINARY,
+	"left",		LEFT,
+	"nonassoc",	BINARY,
+	"prec",		PREC,
+	"right",	RIGHT,
+	"start",	START,
+	"term",		TERM,
+	"token",	TERM,
+	"type",		TYPEDEF,
+	"union",	UNION,
+	0,
+};
+
+	/* define functions */
+
+void	main(int, char**);
+void	others(void);
+char*	chcopy(char*, char*);
+char*	writem(int*);
+char*	symnam(int);
+void	summary(void);
+void	error(char*, ...);
+void	aryfil(int*, int, int);
+int	setunion(int*, int*);
+void	prlook(Lkset*);
+void	cpres(void);
+void	cpfir(void);
+int	state(int);
+void	putitem(int*, Lkset*);
+void	cempty(void);
+void	stagen(void);
+void	closure(int);
+Lkset*	flset(Lkset*);
+void	cleantmp(void);
+void	intr(void);
+void	setup(int, char**);
+void	finact(void);
+int	defin(int, char*);
+void	defout(int);
+char*	cstash(char*);
+long	gettok(void);
+int	fdtype(int);
+int	chfind(int, char*);
+void	cpyunion(void);
+void	cpycode(void);
+int	skipcom(void);
+void	cpyact(int);
+void	openup(char*, int, int, int, char*);
+void	output(void);
+int	apack(int*, int);
+void	go2out(void);
+void	go2gen(int);
+void	precftn(int, int, int);
+void	wract(int);
+void	wrstate(int);
+void	warray(char*, int*, int);
+void	hideprod(void);
+void	callopt(void);
+void	gin(int);
+void	stin(int);
+int	nxti(void);
+void	osummary(void);
+void	aoutput(void);
+void	arout(char*, int*, int);
+int	gtnm(void);
+
+void
+main(int argc, char *argv[])
+{
+
+	setup(argc, argv);	/* initialize and read productions */
+	tbitset = NWORDS(ntokens);
+	cpres();		/* make table of which productions yield a given nonterminal */
+	cempty();		/* make a table of which nonterminals can match the empty string */
+	cpfir();		/* make a table of firsts of nonterminals */
+	stagen();		/* generate the states */
+	output();		/* write the states and the tables */
+	go2out();
+	hideprod();
+	summary();
+	callopt();
+	others();
+	exits(0);
+}
+
+/*
+ * put out other arrays, copy the parsers
+ */
+void
+others(void)
+{
+	int c, i, j;
+
+	finput = Bopen(parser, OREAD);
+	if(finput == 0)
+		error("cannot find parser %s", parser);
+	warray("yyr1", levprd, nprod);
+	aryfil(temp1, nprod, 0);
+	PLOOP(1, i)
+		temp1[i] = prdptr[i+1]-prdptr[i]-2;
+	warray("yyr2", temp1, nprod);
+
+	aryfil(temp1, nstate, -1000);
+	TLOOP(i)
+		for(j=tstates[i]; j!=0; j=mstates[j])
+			temp1[j] = i;
+	NTLOOP(i)
+		for(j=ntstates[i]; j!=0; j=mstates[j])
+			temp1[j] = -i;
+	warray("yychk", temp1, nstate);
+	warray("yydef", defact, nstate);
+
+	/* put out token translation tables */
+	/* table 1 has 0-256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok1", temp1, c+1);
+
+	/* table 2 has PRIVATE-PRIVATE+256 */
+	aryfil(temp1, 256, 0);
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value - PRIVATE;
+		if(j >= 0 && j < 256) {
+			if(temp1[j]) {
+				print("yacc bug -- cant have 2 different Ts with same value\n");
+				print("	%s and %s\n", tokset[i].name, tokset[temp1[j]].name);
+				nerrors++;
+			}
+			temp1[j] = i;
+			if(j > c)
+				c = j;
+		}
+	}
+	warray("yytok2", temp1, c+1);
+
+	/* table 3 has everything else */
+	Bprint(ftable, "long	yytok3[] =\n{\n");
+	c = 0;
+	TLOOP(i) {
+		j = tokset[i].value;
+		if(j >= 0 && j < 256)
+			continue;
+		if(j >= PRIVATE && j < 256+PRIVATE)
+			continue;
+
+		Bprint(ftable, "%4d,%4d,", j, i);
+		c++;
+		if(c%5 == 0)
+			Bprint(ftable, "\n");
+	}
+	Bprint(ftable, "%4d\n};\n", 0);
+
+	/* copy parser text */
+	while((c=Bgetrune(finput)) != Beof) {
+		if(c == '$') {
+			if((c = Bgetrune(finput)) != 'A')
+				Bputrune(ftable, '$');
+			else { /* copy actions */
+				faction = Bopen(actname, OREAD);
+				if(faction == 0)
+					error("cannot reopen action tempfile");
+				while((c=Bgetrune(faction)) != Beof)
+					Bputrune(ftable, c);
+				Bterm(faction);
+				ZAPFILE(actname);
+				c = Bgetrune(finput);
+			}
+		}
+		Bputrune(ftable, c);
+	}
+	Bterm(ftable);
+}
+
+/*
+ * copies string q into p, returning next free char ptr
+ */
+char*
+chcopy(char* p, char* q)
+{
+	int c;
+
+	while(c = *q) {
+		if(c == '"')
+			*p++ = '\\';
+		*p++ = c;
+		q++;
+	}
+	*p = 0;
+	return p;
+}
+
+/*
+ * creates output string for item pointed to by pp
+ */
+char*
+writem(int *pp)
+{
+	int i,*p;
+	static char sarr[ISIZE];
+	char* q;
+
+	for(p=pp; *p>0; p++)
+		;
+	p = prdptr[-*p];
+	q = chcopy(sarr, nontrst[*p-NTBASE].name);
+	q = chcopy(q, ": ");
+	for(;;) {
+		*q = ' ';
+		p++;
+		if(p == pp)
+			*q = '.';
+		q++;
+		*q = '\0';
+		i = *p;
+		if(i <= 0)
+			break;
+		q = chcopy(q, symnam(i));
+		if(q > &sarr[ISIZE-30])
+			error("item too big");
+	}
+
+	/* an item calling for a reduction */
+	i = *pp;
+	if(i < 0 ) {
+		q = chcopy(q, "    (");
+		sprint(q, "%d)", -i);
+	}
+	return sarr;
+}
+
+/*
+ * return a pointer to the name of symbol i
+ */
+char*
+symnam(int i)
+{
+	char* cp;
+
+	cp = (i >= NTBASE)? nontrst[i-NTBASE].name: tokset[i].name;
+	if(*cp == ' ')
+		cp++;
+	return cp;
+}
+
+/*
+ * output the summary on y.output
+ */
+void
+summary(void)
+{
+
+	if(foutput != 0) {
+		Bprint(foutput, "\n%d/%d terminals, %d/%d nonterminals\n",
+			ntokens, NTERMS, nnonter, NNONTERM);
+		Bprint(foutput, "%d/%d grammar rules, %d/%d states\n",
+			nprod, NPROD, nstate, NSTATES);
+		Bprint(foutput, "%d shift/reduce, %d reduce/reduce conflicts reported\n",
+			zzsrconf, zzrrconf);
+		Bprint(foutput, "%d/%d working sets used\n",
+			(int)(zzcwp-wsets), WSETSIZE);
+		Bprint(foutput, "memory: states,etc. %d/%d, parser %d/%d\n",
+			(int)(zzmemsz-mem0), MEMSIZE, (int)(memp-amem), ACTSIZE);
+		Bprint(foutput, "%d/%d distinct lookahead sets\n", nlset, LSETSIZE);
+		Bprint(foutput, "%d extra closures\n", zzclose - 2*nstate);
+		Bprint(foutput, "%d shift entries, %d exceptions\n", zzacent, zzexcp);
+		Bprint(foutput, "%d goto entries\n", zzgoent);
+		Bprint(foutput, "%d entries saved by goto default\n", zzgobest);
+	}
+	if(zzsrconf != 0 || zzrrconf != 0) {
+		print("\nconflicts: ");
+		if(zzsrconf)
+			print("%d shift/reduce", zzsrconf);
+		if(zzsrconf && zzrrconf)
+			print(", ");
+		if(zzrrconf)
+			print("%d reduce/reduce", zzrrconf);
+		print("\n");
+	}
+	if(ftemp != 0) {
+		Bterm(ftemp);
+		ftemp = 0;
+	}
+	if(fdefine != 0) {
+		Bterm(fdefine);
+		fdefine = 0;
+	}
+}
+
+/*
+ * write out error comment -- NEEDS WORK
+ */
+void
+error(char *s, ...)
+{
+
+	nerrors++;
+	fprint(2, "\n fatal error:");
+	fprint(2, s, (&s)[1]);
+	fprint(2, ", %s:%d\n", infile, lineno);
+	if(!fatfl)
+		return;
+	summary();
+	cleantmp();
+	exits("error");
+}
+
+/*
+ * set elements 0 through n-1 to c
+ */
+void
+aryfil(int *v, int n, int c)
+{
+	int i;
+
+	for(i=0; i<n; i++)
+		v[i] = c;
+}
+
+/*
+ * set a to the union of a and b
+ * return 1 if b is not a subset of a, 0 otherwise
+ */
+int
+setunion(int *a, int *b)
+{
+	int i, x, sub;
+
+	sub = 0;
+	SETLOOP(i) {
+		x = *a;
+		*a |= *b;
+		if(*a != x)
+			sub = 1;
+		a++;
+		b++;
+	}
+	return sub;
+}
+
+void
+prlook(Lkset* p)
+{
+	int j, *pp;
+
+	pp = p->lset;
+	if(pp == 0)
+		Bprint(foutput, "\tNULL");
+	else {
+		Bprint(foutput, " { ");
+		TLOOP(j)
+			if(BIT(pp,j))
+				Bprint(foutput, "%s ", symnam(j));
+		Bprint(foutput, "}");
+	}
+}
+
+/*
+ * compute an array with the beginnings of  productions yielding given nonterminals
+ * The array pres points to these lists
+ * the array pyield has the lists: the total size is only NPROD+1
+ */
+void
+cpres(void)
+{
+	int c, j, i, **pmem;
+	static int *pyield[NPROD];
+
+	pmem = pyield;
+	NTLOOP(i) {
+		c = i+NTBASE;
+		pres[i] = pmem;
+		fatfl = 0;  	/* make undefined  symbols  nonfatal */
+		PLOOP(0, j)
+			if(*prdptr[j] == c)
+				*pmem++ =  prdptr[j]+1;
+		if(pres[i] == pmem)
+			error("nonterminal %s not defined!", nontrst[i].name);
+	}
+	pres[i] = pmem;
+	fatfl = 1;
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+	if(pmem != &pyield[nprod])
+		error("internal Yacc error: pyield %d", pmem-&pyield[nprod]);
+}
+
+/*
+ * compute an array with the first of nonterminals
+ */
+void
+cpfir(void)
+{
+	int *p, **s, i, **t, ch, changes;
+
+	zzcwp = &wsets[nnonter];
+	NTLOOP(i) {
+		aryfil(wsets[i].ws.lset, tbitset, 0);
+		t = pres[i+1];
+		/* initially fill the sets */
+		for(s=pres[i]; s<t; ++s)
+			for(p = *s; (ch = *p) > 0; ++p) {
+				if(ch < NTBASE) {
+					SETBIT(wsets[i].ws.lset, ch);
+					break;
+				}
+				if(!pempty[ch-NTBASE])
+					break;
+			}
+	}
+
+	/* now, reflect transitivity */
+	changes = 1;
+	while(changes) {
+		changes = 0;
+		NTLOOP(i) {
+			t = pres[i+1];
+			for(s = pres[i]; s < t; ++s)
+				for(p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
+					changes |= setunion(wsets[i].ws.lset, wsets[ch].ws.lset);
+					if(!pempty[ch])
+						break;
+				}
+		}
+	}
+
+	NTLOOP(i)
+		pfirst[i] = flset(&wsets[i].ws);
+	if(!indebug)
+		return;
+	if(foutput != 0)
+		NTLOOP(i) {
+			Bprint(foutput, "\n%s: ", nontrst[i].name);
+			prlook(pfirst[i]);
+			Bprint(foutput, " %d\n", pempty[i]);
+		}
+}
+
+/*
+ * sorts last state,and sees if it equals earlier ones. returns state number
+ */
+int
+state(int c)
+{
+	Item *p1, *p2, *k, *l, *q1, *q2;
+	int size1, size2, i;
+
+	p1 = pstate[nstate];
+	p2 = pstate[nstate+1];
+	if(p1 == p2)
+		return 0;	/* null state */
+	/* sort the items */
+	for(k = p2-1; k > p1; k--)	/* make k the biggest */
+		for(l = k-1; l >= p1; --l)
+			if(l->pitem > k->pitem) {
+				int *s;
+				Lkset *ss;
+
+				s = k->pitem;
+				k->pitem = l->pitem;
+				l->pitem = s;
+				ss = k->look;
+				k->look = l->look;
+				l->look = ss;
+			}
+	size1 = p2 - p1;	/* size of state */
+
+	for(i = (c>=NTBASE)? ntstates[c-NTBASE]: tstates[c]; i != 0; i = mstates[i]) {
+		/* get ith state */
+		q1 = pstate[i];
+		q2 = pstate[i+1];
+		size2 = q2 - q1;
+		if(size1 != size2)
+			continue;
+		k = p1;
+		for(l = q1; l < q2; l++) {
+			if(l->pitem != k->pitem)
+				break;
+			k++;
+		}
+		if(l != q2)
+			continue;
+		/* found it */
+		pstate[nstate+1] = pstate[nstate];	/* delete last state */
+		/* fix up lookaheads */
+		if(nolook)
+			return i;
+		for(l = q1, k = p1; l < q2; ++l, ++k ) {
+			int s;
+
+			SETLOOP(s)
+				clset.lset[s] = l->look->lset[s];
+			if(setunion(clset.lset, k->look->lset)) {
+				tystate[i] = MUSTDO;
+				/* register the new set */
+				l->look = flset( &clset );
+			}
+		}
+		return i;
+	}
+	/* state is new */
+	if(nolook)
+		error("yacc state/nolook error");
+	pstate[nstate+2] = p2;
+	if(nstate+1 >= NSTATES)
+		error("too many states");
+	if(c >= NTBASE) {
+		mstates[nstate] = ntstates[c-NTBASE];
+		ntstates[c-NTBASE] = nstate;
+	} else {
+		mstates[nstate] = tstates[c];
+		tstates[c] = nstate;
+	}
+	tystate[nstate] = MUSTDO;
+	return nstate++;
+}
+
+void
+putitem(int *ptr, Lkset *lptr)
+{
+	Item *j;
+
+	if(pidebug && foutput != 0)
+		Bprint(foutput, "putitem(%s), state %d\n", writem(ptr), nstate);
+	j = pstate[nstate+1];
+	j->pitem = ptr;
+	if(!nolook)
+		j->look = flset(lptr);
+	pstate[nstate+1] = ++j;
+	if((int*)j > zzmemsz) {
+		zzmemsz = (int*)j;
+		if(zzmemsz >=  &mem0[MEMSIZE])
+			error("out of state space");
+	}
+}
+
+/*
+ * mark nonterminals which derive the empty string
+ * also, look for nonterminals which don't derive any token strings
+ */
+void
+cempty(void)
+{
+
+	int i, *p;
+
+	/* first, use the array pempty to detect productions that can never be reduced */
+	/* set pempty to WHONOWS */
+	aryfil(pempty, nnonter+1, WHOKNOWS);
+
+	/* now, look at productions, marking nonterminals which derive something */
+more:
+	PLOOP(0, i) {
+		if(pempty[*prdptr[i] - NTBASE])
+			continue;
+		for(p = prdptr[i]+1; *p >= 0; ++p)
+			if(*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
+				break;
+		/* production can be derived */
+		if(*p < 0) {
+			pempty[*prdptr[i]-NTBASE] = OK;
+			goto more;
+		}
+	}
+
+	/* now, look at the nonterminals, to see if they are all OK */
+	NTLOOP(i) {
+		/* the added production rises or falls as the start symbol ... */
+		if(i == 0)
+			continue;
+		if(pempty[i] != OK) {
+			fatfl = 0;
+			error("nonterminal %s never derives any token string", nontrst[i].name);
+		}
+	}
+
+	if(nerrors) {
+		summary();
+		cleantmp();
+		exits("error");
+	}
+
+	/* now, compute the pempty array, to see which nonterminals derive the empty string */
+	/* set pempty to WHOKNOWS */
+	aryfil( pempty, nnonter+1, WHOKNOWS);
+
+	/* loop as long as we keep finding empty nonterminals */
+
+again:
+	PLOOP(1, i) {
+		/* not known to be empty */
+		if(pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
+			for(p = prdptr[i]+1; *p >= NTBASE && pempty[*p-NTBASE] == EMPTY ; ++p)
+				;
+			/* we have a nontrivially empty nonterminal */
+			if(*p < 0) {
+				pempty[*prdptr[i]-NTBASE] = EMPTY;
+				/* got one ... try for another */
+				goto again;
+			}
+		}
+	}
+}
+
+/*
+ * generate the states
+ */
+void
+stagen(void)
+{
+
+	int c, i, j, more;
+	Wset *p, *q;
+
+	/* initialize */
+	nstate = 0;
+
+	/* THIS IS FUNNY from the standpoint of portability
+	 * it represents the magic moment when the mem0 array, which has
+	 * been holding the productions, starts to hold item pointers, of a
+	 * different type...
+	 * someday, alloc should be used to allocate all this stuff... for now, we
+	 * accept that if pointers don't fit in integers, there is a problem...
+	 */
+
+	pstate[0] = pstate[1] = (Item*)mem;
+	aryfil(clset.lset, tbitset, 0);
+	putitem(prdptr[0]+1, &clset);
+	tystate[0] = MUSTDO;
+	nstate = 1;
+	pstate[2] = pstate[1];
+
+	aryfil(amem, ACTSIZE, 0);
+
+	/* now, the main state generation loop */
+	for(more=1; more;) {
+		more = 0;
+		SLOOP(i) {
+			if(tystate[i] != MUSTDO)
+				continue;
+			tystate[i] = DONE;
+			aryfil(temp1, nnonter+1, 0);
+			/* take state i, close it, and do gotos */
+			closure(i);
+			/* generate goto's */
+			WSLOOP(wsets, p) {
+				if(p->flag)
+					continue;
+				p->flag = 1;
+				c = *(p->pitem);
+				if(c <= 1) {
+					if(pstate[i+1]-pstate[i] <= p-wsets)
+						tystate[i] = MUSTLOOKAHEAD;
+					continue;
+				}
+				/* do a goto on c */
+				WSLOOP(p, q)
+					/* this item contributes to the goto */
+					if(c == *(q->pitem)) {
+						putitem(q->pitem+1, &q->ws);
+						q->flag = 1;
+					}
+				if(c < NTBASE)
+					state(c);	/* register new state */
+				else
+					temp1[c-NTBASE] = state(c);
+			}
+			if(gsdebug && foutput != 0) {
+				Bprint(foutput, "%d: ", i);
+				NTLOOP(j)
+					if(temp1[j])
+						Bprint(foutput, "%s %d, ",
+						nontrst[j].name, temp1[j]);
+				Bprint(foutput, "\n");
+			}
+			indgo[i] = apack(&temp1[1], nnonter-1) - 1;
+			/* do some more */
+			more = 1;
+		}
+	}
+}
+
+/*
+ * generate the closure of state i
+ */
+void
+closure(int i)
+{
+
+	Wset *u, *v;
+	Item *p, *q;
+	int c, ch, work, k, *pi, **s, **t;
+
+	zzclose++;
+
+	/* first, copy kernel of state i to wsets */
+	cwp = wsets;
+	ITMLOOP(i, p, q) {
+		cwp->pitem = p->pitem;
+		cwp->flag = 1;			/* this item must get closed */
+		SETLOOP(k)
+			cwp->ws.lset[k] = p->look->lset[k];
+		WSBUMP(cwp);
+	}
+
+	/* now, go through the loop, closing each item */
+	work = 1;
+	while(work) {
+		work = 0;
+		WSLOOP(wsets, u) {
+			if(u->flag == 0)
+				continue;
+			/* dot is before c */
+			c = *(u->pitem);
+			if(c < NTBASE) {
+				u->flag = 0;
+				/* only interesting case is where . is before nonterminal */
+				continue;
+			}
+
+			/* compute the lookahead */
+			aryfil(clset.lset, tbitset, 0);
+
+			/* find items involving c */
+			WSLOOP(u, v)
+				if(v->flag == 1 && *(pi=v->pitem) == c) {
+					v->flag = 0;
+					if(nolook)
+						continue;
+					while((ch = *++pi) > 0) {
+						/* terminal symbol */
+						if(ch < NTBASE) {
+							SETBIT(clset.lset, ch);
+							break;
+						}
+						/* nonterminal symbol */
+						setunion(clset.lset, pfirst[ch-NTBASE]->lset);
+						if(!pempty[ch-NTBASE])
+							break;
+					}
+					if(ch <= 0)
+						setunion(clset.lset, v->ws.lset);
+				}
+
+			/*
+			 * now loop over productions derived from c
+			 * c is now nonterminal number
+			 */
+			c -= NTBASE;
+			t = pres[c+1];
+			for(s = pres[c]; s < t; ++s) {
+				/*
+				 * put these items into the closure
+				 * is the item there
+				 */
+				WSLOOP(wsets, v)
+					/* yes, it is there */
+					if(v->pitem == *s) {
+						if(nolook)
+							goto nexts;
+						if(setunion(v->ws.lset, clset.lset))
+							v->flag = work = 1;
+						goto nexts;
+					}
+
+				/*  not there; make a new entry */
+				if(cwp-wsets+1 >= WSETSIZE)
+					error( "working set overflow");
+				cwp->pitem = *s;
+				cwp->flag = 1;
+				if(!nolook) {
+					work = 1;
+					SETLOOP(k) cwp->ws.lset[k] = clset.lset[k];
+				}
+				WSBUMP(cwp);
+
+			nexts:;
+			}
+		}
+	}
+
+	/* have computed closure; flags are reset; return */
+	if(cwp > zzcwp)
+		zzcwp = cwp;
+	if(cldebug && foutput != 0) {
+		Bprint(foutput, "\nState %d, nolook = %d\n", i, nolook);
+		WSLOOP(wsets, u) {
+			if(u->flag)
+				Bprint(foutput, "flag set!\n");
+			u->flag = 0;
+			Bprint(foutput, "\t%s", writem(u->pitem));
+			prlook(&u->ws);
+			Bprint(foutput, "\n");
+		}
+	}
+}
+
+/*
+ * decide if the lookahead set pointed to by p is known
+ * return pointer to a perminent location for the set
+ */
+Lkset*
+flset(Lkset *p)
+{
+	Lkset *q;
+	int *u, *v, *w, j;
+
+	for(q = &lkst[nlset]; q-- > lkst;) {
+		u = p->lset;
+		v = q->lset;
+		w = &v[tbitset];
+		while(v < w)
+			if(*u++ != *v++)
+				goto more;
+		/* we have matched */
+		return q;
+	more:;
+	}
+	/* add a new one */
+	q = &lkst[nlset++];
+	if(nlset >= LSETSIZE)
+		error("too many lookahead sets");
+	SETLOOP(j)
+		q->lset[j] = p->lset[j];
+	return q;
+}
+
+void
+cleantmp(void)
+{
+	ZAPFILE(actname);
+	ZAPFILE(tempname);
+}
+
+void
+intr(void)
+{
+	cleantmp();
+	exits("interrupted");
+}
+
+void
+setup(int argc, char *argv[])
+{
+	long c, t;
+	int i, j, fd, lev, ty, ytab, *p;
+	int vflag, dflag, stem;
+	char actnm[8], *stemc, *s, dirbuf[128];
+
+	ytab = 0;
+	vflag = 0;
+	dflag = 0;
+	stem = 0;
+	stemc = "y";
+	foutput = 0;
+	fdefine = 0;
+	fdebug = 0;
+	ARGBEGIN{
+	case 'v':
+	case 'V':
+		vflag++;
+		break;
+	case 'D':
+		yydebug = ARGF();
+		break;
+	case 'd':
+		dflag++;
+		break;
+	case 'o':
+		ytab++;
+		ytabc = ARGF();
+		break;
+	case 's':
+		stem++;
+		stemc = ARGF();
+		break;
+	case 'S':
+		parser = PARSERS;
+		break;
+	default:
+		error("illegal option: %c", ARGC());
+	}ARGEND
+	openup(stemc, dflag, vflag, ytab, ytabc);
+
+	if((fd = mkstemp(ttempname)) >= 0){
+		tempname = ttempname;
+		ftemp = Bfdopen(fd, OWRITE);
+	}
+	if((fd = mkstemp(tactname)) >= 0){
+		actname = tactname;
+		faction = Bfdopen(fd, OWRITE);
+	}
+	if(ftemp == 0 || faction == 0)
+		error("cannot open temp file");
+	if(argc < 1)
+		error("no input file");
+	infile = argv[0];
+	if(infile[0] != '/' && getwd(dirbuf, sizeof dirbuf)!=nil){
+		i = strlen(infile)+1+strlen(dirbuf)+1+10;
+		s = malloc(i);
+		if(s != nil){
+			snprint(s, i, "%s/%s", dirbuf, infile);
+			cleanname(s);
+			infile = s;
+		}
+	}
+	finput = Bopen(infile, OREAD);
+	if(finput == 0)
+		error("cannot open '%s'", argv[0]);
+	cnamp = cnames;
+
+	defin(0, "$end");
+	extval = PRIVATE;	/* tokens start in unicode 'private use' */
+	defin(0, "error");
+	defin(1, "$accept");
+	defin(0, "$unk");
+	mem = mem0;
+	i = 0;
+
+	for(t = gettok(); t != MARK && t != ENDFILE;)
+	switch(t) {
+	case ';':
+		t = gettok();
+		break;
+
+	case START:
+		if(gettok() != IDENTIFIER)
+			error("bad %%start construction");
+		start = chfind(1, tokname);
+		t = gettok();
+		continue;
+
+	case TYPEDEF:
+		if(gettok() != TYPENAME)
+			error("bad syntax in %%type");
+		ty = numbval;
+		for(;;) {
+			t = gettok();
+			switch(t) {
+			case IDENTIFIER:
+				if((t=chfind(1, tokname)) < NTBASE) {
+					j = TYPE(toklev[t]);
+					if(j != 0 && j != ty)
+						error("type redeclaration of token %s",
+							tokset[t].name);
+					else
+						SETTYPE(toklev[t], ty);
+				} else {
+					j = nontrst[t-NTBASE].value;
+					if(j != 0 && j != ty)
+						error("type redeclaration of nonterminal %s",
+							nontrst[t-NTBASE].name );
+					else
+						nontrst[t-NTBASE].value = ty;
+				}
+			case ',':
+				continue;
+			case ';':
+				t = gettok();
+			default:
+				break;
+			}
+			break;
+		}
+		continue;
+
+	case UNION:
+		/* copy the union declaration to the output */
+		cpyunion();
+		t = gettok();
+		continue;
+
+	case LEFT:
+	case BINARY:
+	case RIGHT:
+		i++;
+
+	case TERM:
+		/* nonzero means new prec. and assoc. */
+		lev = t-TERM;
+		ty = 0;
+
+		/* get identifiers so defined */
+		t = gettok();
+
+		/* there is a type defined */
+		if(t == TYPENAME) {
+			ty = numbval;
+			t = gettok();
+		}
+		for(;;) {
+			switch(t) {
+			case ',':
+				t = gettok();
+				continue;
+
+			case ';':
+				break;
+
+			case IDENTIFIER:
+				j = chfind(0, tokname);
+				if(j >= NTBASE)
+					error("%s defined earlier as nonterminal", tokname);
+				if(lev) {
+					if(ASSOC(toklev[j]))
+						error("redeclaration of precedence of %s", tokname);
+					SETASC(toklev[j], lev);
+					SETPLEV(toklev[j], i);
+				}
+				if(ty) {
+					if(TYPE(toklev[j]))
+						error("redeclaration of type of %s", tokname);
+					SETTYPE(toklev[j],ty);
+				}
+				t = gettok();
+				if(t == NUMBER) {
+					tokset[j].value = numbval;
+					if(j < ndefout && j > 3)
+						error("please define type number of %s earlier",
+							tokset[j].name);
+					t = gettok();
+				}
+				continue;
+			}
+			break;
+		}
+		continue;
+
+	case LCURLY:
+		defout(0);
+		cpycode();
+		t = gettok();
+		continue;
+
+	default:
+		error("syntax error");
+	}
+	if(t == ENDFILE)
+		error("unexpected EOF before %%");
+
+	/* t is MARK */
+	Bprint(ftable, "extern	int	yyerrflag;\n");
+	Bprint(ftable, "#ifndef	YYMAXDEPTH\n");
+	Bprint(ftable, "#define	YYMAXDEPTH	150\n");
+	Bprint(ftable, "#endif\n" );
+	if(!ntypes) {
+		Bprint(ftable, "#ifndef	YYSTYPE\n");
+		Bprint(ftable, "#define	YYSTYPE	int\n");
+		Bprint(ftable, "#endif\n");
+	}
+	Bprint(ftable, "YYSTYPE	yylval;\n");
+	Bprint(ftable, "YYSTYPE	yyval;\n");
+
+	prdptr[0] = mem;
+
+	/* added production */
+	*mem++ = NTBASE;
+
+	/* if start is 0, we will overwrite with the lhs of the first rule */
+	*mem++ = start;
+	*mem++ = 1;
+	*mem++ = 0;
+	prdptr[1] = mem;
+	while((t=gettok()) == LCURLY)
+		cpycode();
+	if(t != IDENTCOLON)
+		error("bad syntax on first rule");
+
+	if(!start)
+		prdptr[0][1] = chfind(1, tokname);
+
+	/* read rules */
+	while(t != MARK && t != ENDFILE) {
+		/* process a rule */
+		rlines[nprod] = lineno;
+		if(t == '|')
+			*mem++ = *prdptr[nprod-1];
+		else
+			if(t == IDENTCOLON) {
+				*mem = chfind(1, tokname);
+				if(*mem < NTBASE)
+					error("token illegal on LHS of grammar rule");
+				mem++;
+			} else
+				error("illegal rule: missing semicolon or | ?");
+		/* read rule body */
+		t = gettok();
+
+	more_rule:
+		while(t == IDENTIFIER) {
+			*mem = chfind(1, tokname);
+			if(*mem < NTBASE)
+				levprd[nprod] = toklev[*mem];
+			mem++;
+			t = gettok();
+		}
+		if(t == PREC) {
+			if(gettok() != IDENTIFIER)
+				error("illegal %%prec syntax");
+			j = chfind(2, tokname);
+			if(j >= NTBASE)
+				error("nonterminal %s illegal after %%prec",
+					nontrst[j-NTBASE].name);
+			levprd[nprod] = toklev[j];
+			t = gettok();
+		}
+		if(t == '=') {
+			levprd[nprod] |= ACTFLAG;
+			Bprint(faction, "\ncase %d:", nprod);
+			cpyact(mem-prdptr[nprod]-1);
+			Bprint(faction, " break;");
+			if((t=gettok()) == IDENTIFIER) {
+
+				/* action within rule... */
+				sprint(actnm, "$$%d", nprod);
+
+				/* make it a nonterminal */
+				j = chfind(1, actnm);
+
+				/*
+				 * the current rule will become rule number nprod+1
+				 * move the contents down, and make room for the null
+				 */
+				for(p = mem; p >= prdptr[nprod]; --p)
+					p[2] = *p;
+				mem += 2;
+
+				/* enter null production for action */
+				p = prdptr[nprod];
+				*p++ = j;
+				*p++ = -nprod;
+
+				/* update the production information */
+				levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
+				levprd[nprod] = ACTFLAG;
+				if(++nprod >= NPROD)
+					error("more than %d rules", NPROD);
+				prdptr[nprod] = p;
+
+				/* make the action appear in the original rule */
+				*mem++ = j;
+
+				/* get some more of the rule */
+				goto more_rule;
+			}
+		}
+
+		while(t == ';')
+			t = gettok();
+		*mem++ = -nprod;
+
+		/* check that default action is reasonable */
+		if(ntypes && !(levprd[nprod]&ACTFLAG) && nontrst[*prdptr[nprod]-NTBASE].value) {
+
+			/* no explicit action, LHS has value */
+			int tempty;
+
+			tempty = prdptr[nprod][1];
+			if(tempty < 0)
+				error("must return a value, since LHS has a type");
+			else
+				if(tempty >= NTBASE)
+					tempty = nontrst[tempty-NTBASE].value;
+				else
+					tempty = TYPE(toklev[tempty]);
+			if(tempty != nontrst[*prdptr[nprod]-NTBASE].value)
+				error("default action causes potential type clash");
+		}
+		nprod++;
+		if(nprod >= NPROD)
+			error("more than %d rules", NPROD);
+		prdptr[nprod] = mem;
+		levprd[nprod] = 0;
+	}
+
+	/* end of all rules */
+	defout(1);
+
+	finact();
+	if(t == MARK) {
+		Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+		while((c=Bgetrune(finput)) != Beof)
+			Bputrune(ftable, c);
+	}
+	Bterm(finput);
+}
+
+/*
+ * finish action routine
+ */
+void
+finact(void)
+{
+
+	Bterm(faction);
+	Bprint(ftable, "#define YYEOFCODE %d\n", 1);
+	Bprint(ftable, "#define YYERRCODE %d\n", 2);
+}
+
+/*
+ * define s to be a terminal if t=0
+ * or a nonterminal if t=1
+ */
+int
+defin(int nt, char *s)
+{
+	int val;
+	Rune rune;
+
+	val = 0;
+	if(nt) {
+		nnonter++;
+		if(nnonter >= NNONTERM)
+			error("too many nonterminals, limit %d",NNONTERM);
+		nontrst[nnonter].name = cstash(s);
+		return NTBASE + nnonter;
+	}
+
+	/* must be a token */
+	ntokens++;
+	if(ntokens >= NTERMS)
+		error("too many terminals, limit %d", NTERMS);
+	tokset[ntokens].name = cstash(s);
+
+	/* establish value for token */
+	/* single character literal */
+	if(s[0] == ' ') {
+		val = chartorune(&rune, &s[1]);
+		if(s[val+1] == 0) {
+			val = rune;
+			goto out;
+		}
+	}
+
+	/* escape sequence */
+	if(s[0] == ' ' && s[1] == '\\') {
+		if(s[3] == 0) {
+			/* single character escape sequence */
+			switch(s[2]) {
+			case 'n':	val = '\n'; break;
+			case 'r':	val = '\r'; break;
+			case 'b':	val = '\b'; break;
+			case 't':	val = '\t'; break;
+			case 'f':	val = '\f'; break;
+			case '\'':	val = '\''; break;
+			case '"':	val = '"'; break;
+			case '\\':	val = '\\'; break;
+			default:	error("invalid escape");
+			}
+			goto out;
+		}
+
+		/* \nnn sequence */
+		if(s[2] >= '0' && s[2] <= '7') {
+			if(s[3] < '0' ||
+			   s[3] > '7' ||
+			   s[4] < '0' ||
+			   s[4] > '7' ||
+			   s[5] != 0)
+				error("illegal \\nnn construction");
+			val = 64*s[2] + 8*s[3] + s[4] - 73*'0';
+			if(val == 0)
+				error("'\\000' is illegal");
+			goto out;
+		}
+		error("unknown escape");
+	}
+	val = extval++;
+
+out:
+	tokset[ntokens].value = val;
+	toklev[ntokens] = 0;
+	return ntokens;
+}
+
+/*
+ * write out the defines (at the end of the declaration section)
+ */
+void
+defout(int last)
+{
+	int i, c;
+	char sar[NAMESIZE+10];
+
+	for(i=ndefout; i<=ntokens; i++) {
+		/* non-literals */
+		c = tokset[i].name[0];
+		if(c != ' ' && c != '$') {
+			Bprint(ftable, "#define	%s	%d\n",
+				tokset[i].name, tokset[i].value);
+			if(fdefine)
+				Bprint(fdefine, "#define\t%s\t%d\n",
+					tokset[i].name, tokset[i].value);
+		}
+	}
+	ndefout = ntokens+1;
+	if(last && fdebug) {
+		Bprint(fdebug, "char*	yytoknames[] =\n{\n");
+		TLOOP(i) {
+			if(tokset[i].name) {
+				chcopy(sar, tokset[i].name);
+				Bprint(fdebug, "\t\"%s\",\n", sar);
+				continue;
+			}
+			Bprint(fdebug, "\t0,\n");
+		}
+		Bprint(fdebug, "};\n");
+	}
+}
+
+char*
+cstash(char *s)
+{
+	char *temp;
+
+	temp = cnamp;
+	do {
+		if(cnamp >= &cnames[cnamsz])
+			error("too many characters in id's and literals");
+		else
+			*cnamp++ = *s;
+	} while(*s++);
+	return temp;
+}
+
+long
+gettok(void)
+{
+	long c;
+	Rune rune;
+	int i, base, match, reserve;
+	static int peekline;
+
+begin:
+	reserve = 0;
+	lineno += peekline;
+	peekline = 0;
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\n' || c == '\t' || c == '\f') {
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+
+	/* skip comment */
+	if(c == '/') {
+		lineno += skipcom();
+		goto begin;
+	}
+	switch(c) {
+	case Beof:
+		return ENDFILE;
+
+	case '{':
+		Bungetrune(finput);
+		return '=';
+
+	case '<':
+		/* get, and look up, a type name (union member name) */
+		i = 0;
+		while((c=Bgetrune(finput)) != '>' && c >= 0 && c != '\n') {
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		if(c != '>')
+			error("unterminated < ... > clause");
+		tokname[i] = 0;
+		for(i=1; i<=ntypes; i++)
+			if(!strcmp(typeset[i], tokname)) {
+				numbval = i;
+				return TYPENAME;
+			}
+		ntypes++;
+		numbval = ntypes;
+		typeset[numbval] = cstash(tokname);
+		return TYPENAME;
+
+	case '"':
+	case '\'':
+		match = c;
+		tokname[0] = ' ';
+		i = 1;
+		for(;;) {
+			c = Bgetrune(finput);
+			if(c == '\n' || c <= 0)
+				error("illegal or missing ' or \"" );
+			if(c == '\\') {
+				tokname[i] = '\\';
+				if(i < NAMESIZE)
+					i++;
+				c = Bgetrune(finput);
+			} else
+				if(c == match)
+					break;
+			rune = c;
+			c = runetochar(&tokname[i], &rune);
+			if(i < NAMESIZE)
+				i += c;
+		}
+		break;
+
+	case '%':
+	case '\\':
+		switch(c = Bgetrune(finput)) {
+		case '0':	return TERM;
+		case '<':	return LEFT;
+		case '2':	return BINARY;
+		case '>':	return RIGHT;
+		case '%':
+		case '\\':	return MARK;
+		case '=':	return PREC;
+		case '{':	return LCURLY;
+		default:	reserve = 1;
+		}
+
+	default:
+		/* number */
+		if(isdigit(c)) {
+			numbval = c-'0';
+			base = (c=='0')? 8: 10;
+			for(c = Bgetrune(finput); isdigit(c); c = Bgetrune(finput))
+				numbval = numbval*base + (c-'0');
+			Bungetrune(finput);
+			return NUMBER;
+		}
+		if(islower(c) || isupper(c) || c=='_' || c=='.' || c=='$')  {
+			i = 0;
+			while(islower(c) || isupper(c) || isdigit(c) ||
+			    c == '-' || c=='_' || c=='.' || c=='$') {
+				if(reserve && isupper(c))
+					c += 'a'-'A';
+				rune = c;
+				c = runetochar(&tokname[i], &rune);
+				if(i < NAMESIZE)
+					i += c;
+				c = Bgetrune(finput);
+			}
+		} else
+			return c;
+		Bungetrune(finput);
+	}
+	tokname[i] = 0;
+
+	/* find a reserved word */
+	if(reserve) {
+		for(c=0; resrv[c].name; c++)
+			if(strcmp(tokname, resrv[c].name) == 0)
+				return resrv[c].value;
+		error("invalid escape, or illegal reserved word: %s", tokname);
+	}
+
+	/* look ahead to distinguish IDENTIFIER from IDENTCOLON */
+	c = Bgetrune(finput);
+	while(c == ' ' || c == '\t'|| c == '\n' || c == '\f' || c == '/') {
+		if(c == '\n')
+			peekline++;
+		/* look for comments */
+		if(c == '/')
+			peekline += skipcom();
+		c = Bgetrune(finput);
+	}
+	if(c == ':')
+		return IDENTCOLON;
+	Bungetrune(finput);
+	return IDENTIFIER;
+}
+
+/*
+ * determine the type of a symbol
+ */
+int
+fdtype(int t)
+{
+	int v;
+
+	if(t >= NTBASE)
+		v = nontrst[t-NTBASE].value;
+	else
+		v = TYPE(toklev[t]);
+	if(v <= 0)
+		error("must specify type for %s", (t>=NTBASE)?
+			nontrst[t-NTBASE].name: tokset[t].name);
+	return v;
+}
+
+int
+chfind(int t, char *s)
+{
+	int i;
+
+	if(s[0] == ' ')
+		t = 0;
+	TLOOP(i)
+		if(!strcmp(s, tokset[i].name))
+			return i;
+	NTLOOP(i)
+		if(!strcmp(s, nontrst[i].name))
+			return NTBASE+i;
+
+	/* cannot find name */
+	if(t > 1)
+		error("%s should have been defined earlier", s);
+	return defin(t, s);
+}
+
+/*
+ * copy the union declaration to the output, and the define file if present
+ */
+void
+cpyunion(void)
+{
+	long c;
+	int level;
+
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	Bprint(ftable, "typedef union ");
+	if(fdefine != 0)
+		Bprint(fdefine, "\ntypedef union ");
+
+	level = 0;
+	for(;;) {
+		if((c=Bgetrune(finput)) == Beof)
+			error("EOF encountered while processing %%union");
+		Bputrune(ftable, c);
+		if(fdefine != 0)
+			Bputrune(fdefine, c);
+		switch(c) {
+		case '\n':
+			lineno++;
+			break;
+		case '{':
+			level++;
+			break;
+		case '}':
+			level--;
+
+			/* we are finished copying */
+			if(level == 0) {
+				Bprint(ftable, " YYSTYPE;\n");
+				if(fdefine != 0)
+					Bprint(fdefine, "\tYYSTYPE;\nextern\tYYSTYPE\tyylval;\n");
+				return;
+			}
+		}
+	}
+}
+
+/*
+ * copies code between \{ and \}
+ */
+void
+cpycode(void)
+{
+
+	long c;
+
+	c = Bgetrune(finput);
+	if(c == '\n') {
+		c = Bgetrune(finput);
+		lineno++;
+	}
+	Bprint(ftable, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	while(c != Beof) {
+		if(c == '\\') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '\\');
+		}
+		if(c == '%') {
+			if((c=Bgetrune(finput)) == '}')
+				return;
+			Bputc(ftable, '%');
+		}
+		Bputrune(ftable, c);
+		if(c == '\n')
+			lineno++;
+		c = Bgetrune(finput);
+	}
+	error("eof before %%}");
+}
+
+/*
+ * skip over comments
+ * skipcom is called after reading a '/'
+ */
+int
+skipcom(void)
+{
+	long c;
+	int i;
+
+	/* i is the number of lines skipped */
+	i = 0;
+	if(Bgetrune(finput) != '*')
+		error("illegal comment");
+	c = Bgetrune(finput);
+	while(c != Beof) {
+		while(c == '*')
+			if((c=Bgetrune(finput)) == '/')
+				return i;
+		if(c == '\n')
+			i++;
+		c = Bgetrune(finput);
+	}
+	error("EOF inside comment");
+	return 0;
+}
+
+/*
+ * copy C action to the next ; or closing }
+ */
+void
+cpyact(int offset)
+{
+	long c;
+	int brac, match, j, s, fnd, tok;
+
+	Bprint(faction, "\n#line\t%d\t\"%s\"\n", lineno, infile);
+	brac = 0;
+
+loop:
+	c = Bgetrune(finput);
+swt:
+	switch(c) {
+	case ';':
+		if(brac == 0) {
+			Bputrune(faction, c);
+			return;
+		}
+		goto lcopy;
+
+	case '{':
+		brac++;
+		goto lcopy;
+
+	case '$':
+		s = 1;
+		tok = -1;
+		c = Bgetrune(finput);
+
+		/* type description */
+		if(c == '<') {
+			Bungetrune(finput);
+			if(gettok() != TYPENAME)
+				error("bad syntax on $<ident> clause");
+			tok = numbval;
+			c = Bgetrune(finput);
+		}
+		if(c == '$') {
+			Bprint(faction, "yyval");
+
+			/* put out the proper tag... */
+			if(ntypes) {
+				if(tok < 0)
+					tok = fdtype(*prdptr[nprod]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(c == '-') {
+			s = -s;
+			c = Bgetrune(finput);
+		}
+		if(isdigit(c)) {
+			j = 0;
+			while(isdigit(c)) {
+				j = j*10 + (c-'0');
+				c = Bgetrune(finput);
+			}
+			Bungetrune(finput);
+			j = j*s - offset;
+			if(j > 0)
+				error("Illegal use of $%d", j+offset);
+
+		dollar:
+			Bprint(faction, "yypt[-%d].yyv", -j);
+
+			/* put out the proper tag */
+			if(ntypes) {
+				if(j+offset <= 0 && tok < 0)
+					error("must specify type of $%d", j+offset);
+				if(tok < 0)
+					tok = fdtype(prdptr[nprod][j+offset]);
+				Bprint(faction, ".%s", typeset[tok]);
+			}
+			goto loop;
+		}
+		if(isupper(c) || islower(c) || c == '_' || c == '.') {
+			int tok; /* tok used oustide for type info */
+
+			/* look for $name */
+			Bungetrune(finput);
+			if(gettok() != IDENTIFIER)
+				error("$ must be followed by an identifier");
+			tok = chfind(2, tokname);
+			if((c = Bgetrune(finput)) != '#') {
+				Bungetrune(finput);
+				fnd = -1;
+			} else
+				if(gettok() != NUMBER) {
+					error("# must be followed by number");
+					fnd = -1;
+				} else
+					fnd = numbval;
+			for(j=1; j<=offset; ++j)
+				if(tok == prdptr[nprod][j]) {
+					if(--fnd <= 0) {
+						j -= offset;
+						goto dollar;
+					}
+				}
+			error("$name or $name#number not found");
+		}
+		Bputc(faction, '$');
+		if(s < 0 )
+			Bputc(faction, '-');
+		goto swt;
+
+	case '}':
+		brac--;
+		if(brac)
+			goto lcopy;
+		Bputrune(faction, c);
+		return;
+
+	case '/':
+		/* look for comments */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		if(c != '*')
+			goto swt;
+
+		/* it really is a comment */
+		Bputrune(faction, c);
+		c = Bgetrune(finput);
+		while(c >= 0) {
+			while(c == '*') {
+				Bputrune(faction, c);
+				if((c=Bgetrune(finput)) == '/')
+					goto lcopy;
+			}
+			Bputrune(faction, c);
+			if(c == '\n')
+				lineno++;
+			c = Bgetrune(finput);
+		}
+		error("EOF inside comment");
+
+	case '\'':
+		/* character constant */
+		match = '\'';
+		goto string;
+
+	case '"':
+		/* character string */
+		match = '"';
+
+	string:
+		Bputrune(faction, c);
+		while(c = Bgetrune(finput)) {
+			if(c == '\\') {
+				Bputrune(faction, c);
+				c = Bgetrune(finput);
+				if(c == '\n')
+					lineno++;
+			} else
+				if(c == match)
+					goto lcopy;
+				if(c == '\n')
+					error("newline in string or char. const.");
+			Bputrune(faction, c);
+		}
+		error("EOF in string or character constant");
+
+	case Beof:
+		error("action does not terminate");
+
+	case '\n':
+		lineno++;
+		goto lcopy;
+	}
+
+lcopy:
+	Bputrune(faction, c);
+	goto loop;
+}
+
+void
+openup(char *stem, int dflag, int vflag, int ytab, char *ytabc)
+{
+	char buf[256];
+
+	if(vflag) {
+		sprint(buf, "%s.%s", stem, FILEU);
+		foutput = Bopen(buf, OWRITE);
+		if(foutput == 0)
+			error("cannot open %s", buf);
+	}
+	if(yydebug) {
+		sprint(buf, "%s.%s", stem, FILEDEBUG);
+		if((fdebug = Bopen(buf, OWRITE)) == 0)
+			error("can't open %s", buf);
+	}
+	if(dflag) {
+		sprint(buf, "%s.%s", stem, FILED);
+		fdefine = Bopen(buf, OWRITE);
+		if(fdefine == 0)
+			error("can't create %s", buf);
+	}
+	if(ytab == 0)
+		sprint(buf, "%s.%s", stem, OFILE);
+	else
+		strcpy(buf, ytabc);
+	ftable = Bopen(buf, OWRITE);
+	if(ftable == 0)
+		error("cannot open table file %s", buf);
+}
+
+/*
+ * print the output for the states
+ */
+void
+output(void)
+{
+	int i, k, c;
+	Wset *u, *v;
+
+	Bprint(ftable, "short	yyexca[] =\n{");
+	if(fdebug)
+		Bprint(fdebug, "char*	yystates[] =\n{\n");
+
+	/* output the stuff for state i */
+	SLOOP(i) {
+		nolook = tystate[i]!=MUSTLOOKAHEAD;
+		closure(i);
+
+		/* output actions */
+		nolook = 1;
+		aryfil(temp1, ntokens+nnonter+1, 0);
+		WSLOOP(wsets, u) {
+			c = *(u->pitem);
+			if(c > 1 && c < NTBASE && temp1[c] == 0) {
+				WSLOOP(u, v)
+					if(c == *(v->pitem))
+						putitem(v->pitem+1, (Lkset*)0);
+				temp1[c] = state(c);
+			} else
+				if(c > NTBASE && temp1[(c -= NTBASE) + ntokens] == 0)
+					temp1[c+ntokens] = amem[indgo[i]+c];
+		}
+		if(i == 1)
+			temp1[1] = ACCEPTCODE;
+
+		/* now, we have the shifts; look at the reductions */
+		lastred = 0;
+		WSLOOP(wsets, u) {
+			c = *u->pitem;
+
+			/* reduction */
+			if(c <= 0) {
+				lastred = -c;
+				TLOOP(k)
+					if(BIT(u->ws.lset, k)) {
+						if(temp1[k] == 0)
+							temp1[k] = c;
+						else
+						if(temp1[k] < 0) { /* reduce/reduce conflict */
+							if(foutput)
+								Bprint(foutput,
+									"\n%d: reduce/reduce conflict"
+									" (red'ns %d and %d ) on %s",
+									i, -temp1[k], lastred,
+									symnam(k));
+							if(-temp1[k] > lastred)
+								temp1[k] = -lastred;
+							zzrrconf++;
+						} else
+							/* potential shift/reduce conflict */
+							precftn( lastred, k, i );
+					}
+				}
+		}
+		wract(i);
+	}
+
+	if(fdebug)
+		Bprint(fdebug, "};\n");
+	Bprint(ftable, "};\n");
+	Bprint(ftable, "#define	YYNPROD	%d\n", nprod);
+	Bprint(ftable, "#define	YYPRIVATE %d\n", PRIVATE);
+	if(yydebug)
+		Bprint(ftable, "#define	yydebug	%s\n", yydebug);
+}
+
+/*
+ * pack state i from temp1 into amem
+ */
+int
+apack(int *p, int n)
+{
+	int *pp, *qq, *rr, off, *q, *r;
+
+	/* we don't need to worry about checking because
+	 * we will only look at entries known to be there...
+	 * eliminate leading and trailing 0's
+	 */
+
+	q = p+n;
+	for(pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
+		;
+ 	/* no actions */
+	if(pp > q)
+		return 0;
+	p = pp;
+
+	/* now, find a place for the elements from p to q, inclusive */
+	r = &amem[ACTSIZE-1];
+	for(rr = amem; rr <= r; rr++, off++) {
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp != 0)
+				if(*pp != *qq && *qq != 0)
+					goto nextk;
+
+		/* we have found an acceptable k */
+		if(pkdebug && foutput != 0)
+			Bprint(foutput, "off = %d, k = %d\n", off, (int)(rr-amem));
+		for(qq = rr, pp = p; pp <= q; pp++, qq++)
+			if(*pp) {
+				if(qq > r)
+					error("action table overflow");
+				if(qq > memp)
+					memp = qq;
+				*qq = *pp;
+			}
+		if(pkdebug && foutput != 0)
+			for(pp = amem; pp <= memp; pp += 10) {
+				Bprint(foutput, "\t");
+				for(qq = pp; qq <= pp+9; qq++)
+					Bprint(foutput, "%d ", *qq);
+				Bprint(foutput, "\n");
+			}
+		return(off);
+	nextk:;
+	}
+	error("no space in action table");
+	return 0;
+}
+
+/*
+ * output the gotos for the nontermninals
+ */
+void
+go2out(void)
+{
+	int i, j, k, best, count, cbest, times;
+
+	/* mark begining of gotos */
+	Bprint(ftemp, "$\n");
+	for(i = 1; i <= nnonter; i++) {
+		go2gen(i);
+
+		/* find the best one to make default */
+		best = -1;
+		times = 0;
+
+		/* is j the most frequent */
+		for(j = 0; j <= nstate; j++) {
+			if(tystate[j] == 0)
+				continue;
+			if(tystate[j] == best)
+				continue;
+
+			/* is tystate[j] the most frequent */
+			count = 0;
+			cbest = tystate[j];
+			for(k = j; k <= nstate; k++)
+				if(tystate[k] == cbest)
+					count++;
+			if(count > times) {
+				best = cbest;
+				times = count;
+			}
+		}
+
+		/* best is now the default entry */
+		zzgobest += times-1;
+		for(j = 0; j <= nstate; j++)
+			if(tystate[j] != 0 && tystate[j] != best) {
+				Bprint(ftemp, "%d,%d,", j, tystate[j]);
+				zzgoent++;
+			}
+
+		/* now, the default */
+		if(best == -1)
+			best = 0;
+		zzgoent++;
+		Bprint(ftemp, "%d\n", best);
+	}
+}
+
+/*
+ * output the gotos for nonterminal c
+ */
+void
+go2gen(int c)
+{
+	int i, work, cc;
+	Item *p, *q;
+
+
+	/* first, find nonterminals with gotos on c */
+	aryfil(temp1, nnonter+1, 0);
+	temp1[c] = 1;
+	work = 1;
+	while(work) {
+		work = 0;
+		PLOOP(0, i)
+
+			/* cc is a nonterminal */
+			if((cc=prdptr[i][1]-NTBASE) >= 0)
+				/* cc has a goto on c */
+				if(temp1[cc] != 0) {
+
+					/* thus, the left side of production i does too */
+					cc = *prdptr[i]-NTBASE;
+					if(temp1[cc] == 0) {
+						  work = 1;
+						  temp1[cc] = 1;
+					}
+				}
+	}
+
+	/* now, we have temp1[c] = 1 if a goto on c in closure of cc */
+	if(g2debug && foutput != 0) {
+		Bprint(foutput, "%s: gotos on ", nontrst[c].name);
+		NTLOOP(i)
+			if(temp1[i])
+				Bprint(foutput, "%s ", nontrst[i].name);
+		Bprint(foutput, "\n");
+	}
+
+	/* now, go through and put gotos into tystate */
+	aryfil(tystate, nstate, 0);
+	SLOOP(i)
+		ITMLOOP(i, p, q)
+			if((cc = *p->pitem) >= NTBASE)
+				/* goto on c is possible */
+				if(temp1[cc-NTBASE]) {
+					tystate[i] = amem[indgo[i]+c];
+					break;
+				}
+}
+
+/*
+ * decide a shift/reduce conflict by precedence.
+ * r is a rule number, t a token number
+ * the conflict is in state s
+ * temp1[t] is changed to reflect the action
+ */
+void
+precftn(int r, int t, int s)
+{
+	int lp, lt, action;
+
+	lp = levprd[r];
+	lt = toklev[t];
+	if(PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
+
+		/* conflict */
+		if(foutput != 0)
+			Bprint(foutput,
+				"\n%d: shift/reduce conflict (shift %d(%d), red'n %d(%d)) on %s",
+				s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t));
+		zzsrconf++;
+		return;
+	}
+	if(PLEVEL(lt) == PLEVEL(lp))
+		action = ASSOC(lt);
+	else
+		if(PLEVEL(lt) > PLEVEL(lp))
+			action = RASC;  /* shift */
+		else
+			action = LASC;  /* reduce */
+	switch(action) {
+	case BASC:  /* error action */
+		temp1[t] = ERRCODE;
+		break;
+	case LASC:  /* reduce */
+		temp1[t] = -r;
+		break;
+	}
+}
+
+/*
+ * output state i
+ * temp1 has the actions, lastred the default
+ */
+void
+wract(int i)
+{
+	int p, p0, p1, ntimes, tred, count, j, flag;
+
+	/* find the best choice for lastred */
+	lastred = 0;
+	ntimes = 0;
+	TLOOP(j) {
+		if(temp1[j] >= 0)
+			continue;
+		if(temp1[j]+lastred == 0)
+			continue;
+		/* count the number of appearances of temp1[j] */
+		count = 0;
+		tred = -temp1[j];
+		levprd[tred] |= REDFLAG;
+		TLOOP(p)
+			if(temp1[p]+tred == 0)
+				count++;
+		if(count > ntimes) {
+			lastred = tred;
+			ntimes = count;
+		}
+	}
+
+	/*
+	 * for error recovery, arrange that, if there is a shift on the
+	 * error recovery token, `error', that the default be the error action
+	 */
+	if(temp1[2] > 0)
+		lastred = 0;
+
+	/* clear out entries in temp1 which equal lastred */
+	TLOOP(p)
+		if(temp1[p]+lastred == 0)
+			temp1[p] = 0;
+
+	wrstate(i);
+	defact[i] = lastred;
+	flag = 0;
+	TLOOP(p0)
+		if((p1=temp1[p0]) != 0) {
+			if(p1 < 0) {
+				p1 = -p1;
+				goto exc;
+			}
+			if(p1 == ACCEPTCODE) {
+				p1 = -1;
+				goto exc;
+			}
+			if(p1 == ERRCODE) {
+				p1 = 0;
+			exc:
+				if(flag++ == 0)
+					Bprint(ftable, "-1, %d,\n", i);
+				Bprint(ftable, "\t%d, %d,\n", p0, p1);
+				zzexcp++;
+				continue;
+			}
+			Bprint(ftemp, "%d,%d,", p0, p1);
+			zzacent++;
+		}
+	if(flag) {
+		defact[i] = -2;
+		Bprint(ftable, "\t-2, %d,\n", lastred);
+	}
+	Bprint(ftemp, "\n");
+}
+
+/*
+ * writes state i
+ */
+void
+wrstate(int i)
+{
+	int j0, j1;
+	Item *pp, *qq;
+	Wset *u;
+
+	if(fdebug) {
+		if(lastred) {
+			Bprint(fdebug, "	0, /*%d*/\n", i);
+		} else {
+			Bprint(fdebug, "	\"");
+			ITMLOOP(i, pp, qq)
+				Bprint(fdebug, "%s\\n", writem(pp->pitem));
+			if(tystate[i] == MUSTLOOKAHEAD)
+				WSLOOP(wsets + (pstate[i+1] - pstate[i]), u)
+					if(*u->pitem < 0)
+						Bprint(fdebug, "%s\\n", writem(u->pitem));
+			Bprint(fdebug, "\", /*%d*/\n", i);
+		}
+	}
+	if(foutput == 0)
+		return;
+	Bprint(foutput, "\nstate %d\n", i);
+	ITMLOOP(i, pp, qq)
+		Bprint(foutput, "\t%s\n", writem(pp->pitem));
+	if(tystate[i] == MUSTLOOKAHEAD)
+		/* print out empty productions in closure */
+		WSLOOP(wsets+(pstate[i+1]-pstate[i]), u)
+			if(*u->pitem < 0)
+				Bprint(foutput, "\t%s\n", writem(u->pitem));
+
+	/* check for state equal to another */
+	TLOOP(j0)
+		if((j1=temp1[j0]) != 0) {
+			Bprint(foutput, "\n\t%s  ", symnam(j0));
+			/* shift, error, or accept */
+			if(j1 > 0) {
+				if(j1 == ACCEPTCODE)
+					Bprint(foutput,  "accept");
+				else
+					if(j1 == ERRCODE)
+						Bprint(foutput, "error");
+					else
+						Bprint(foutput, "shift %d", j1);
+			} else
+				Bprint(foutput, "reduce %d (src line %d)", -j1, rlines[-j1]);
+		}
+
+	/* output the final production */
+	if(lastred)
+		Bprint(foutput, "\n\t.  reduce %d (src line %d)\n\n",
+			lastred, rlines[lastred]);
+	else
+		Bprint(foutput, "\n\t.  error\n\n");
+
+	/* now, output nonterminal actions */
+	j1 = ntokens;
+	for(j0 = 1; j0 <= nnonter; j0++) {
+		j1++;
+		if(temp1[j1])
+			Bprint(foutput, "\t%s  goto %d\n", symnam(j0+NTBASE), temp1[j1]);
+	}
+}
+
+void
+warray(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i=0;;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i >= n) {
+			Bprint(ftable, "\n};\n");
+			break;
+		}
+		Bprint(ftable, ",");
+	}
+}
+
+/*
+ * in order to free up the mem and amem arrays for the optimizer,
+ * and still be able to output yyr1, etc., after the sizes of
+ * the action array is known, we hide the nonterminals
+ * derived by productions in levprd.
+ */
+
+void
+hideprod(void)
+{
+	int i, j;
+
+	j = 0;
+	levprd[0] = 0;
+	PLOOP(1, i) {
+		if(!(levprd[i] & REDFLAG)) {
+			j++;
+			if(foutput != 0)
+				Bprint(foutput, "Rule not reduced:   %s\n", writem(prdptr[i]));
+		}
+		levprd[i] = *prdptr[i] - NTBASE;
+	}
+	if(j)
+		print("%d rules never reduced\n", j);
+}
+
+void
+callopt(void)
+{
+	int i, *p, j, k, *q;
+
+	/* read the arrays from tempfile and set parameters */
+	finput = Bopen(tempname, OREAD);
+	if(finput == 0)
+		error("optimizer cannot open tempfile");
+
+	pgo[0] = 0;
+	temp1[0] = 0;
+	nstate = 0;
+	nnonter = 0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nstate++;
+			pmem--;
+			temp1[nstate] = pmem - mem0;
+		case ',':
+			continue;
+		case '$':
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+
+	pmem--;
+	temp1[nstate] = yypgo[0] = pmem - mem0;
+	for(;;) {
+		switch(gtnm()) {
+		case '\n':
+			nnonter++;
+			yypgo[nnonter] = pmem-mem0;
+		case ',':
+			continue;
+		case -1:
+			break;
+		default:
+			error("bad tempfile");
+		}
+		break;
+	}
+	pmem--;
+	yypgo[nnonter--] = pmem - mem0;
+	for(i = 0; i < nstate; i++) {
+		k = 32000;
+		j = 0;
+		q = mem0 + temp1[i+1];
+		for(p = mem0 + temp1[i]; p < q ; p += 2) {
+			if(*p > j)
+				j = *p;
+			if(*p < k)
+				k = *p;
+		}
+		/* nontrivial situation */
+		if(k <= j) {
+			/* j is now the range */
+/*			j -= k;			*//* call scj */
+			if(k > maxoff)
+				maxoff = k;
+		}
+		tystate[i] = (temp1[i+1]-temp1[i]) + 2*j;
+		if(j > maxspr)
+			maxspr = j;
+	}
+
+	/* initialize ggreed table */
+	for(i = 1; i <= nnonter; i++) {
+		ggreed[i] = 1;
+		j = 0;
+
+		/* minimum entry index is always 0 */
+		q = mem0 + yypgo[i+1] - 1;
+		for(p = mem0+yypgo[i]; p < q ; p += 2) {
+			ggreed[i] += 2;
+			if(*p > j)
+				j = *p;
+		}
+		ggreed[i] = ggreed[i] + 2*j;
+		if(j > maxoff)
+			maxoff = j;
+	}
+
+	/* now, prepare to put the shift actions into the amem array */
+	for(i = 0; i < ACTSIZE; i++)
+		amem[i] = 0;
+	maxa = amem;
+	for(i = 0; i < nstate; i++) {
+		if(tystate[i] == 0 && adb > 1)
+			Bprint(ftable, "State %d: null\n", i);
+		indgo[i] = YYFLAG1;
+	}
+	while((i = nxti()) != NOMORE)
+		if(i >= 0)
+			stin(i);
+		else
+			gin(-i);
+
+	/* print amem array */
+	if(adb > 2 )
+		for(p = amem; p <= maxa; p += 10) {
+			Bprint(ftable, "%4d  ", (int)(p-amem));
+			for(i = 0; i < 10; ++i)
+				Bprint(ftable, "%4d  ", p[i]);
+			Bprint(ftable, "\n");
+		}
+
+	/* write out the output appropriate to the language */
+	aoutput();
+	osummary();
+	ZAPFILE(tempname);
+}
+
+void
+gin(int i)
+{
+	int *p, *r, *s, *q1, *q2;
+
+	/* enter gotos on nonterminal i into array amem */
+	ggreed[i] = 0;
+
+	q2 = mem0+ yypgo[i+1] - 1;
+	q1 = mem0 + yypgo[i];
+
+	/* now, find amem place for it */
+	for(p = amem; p < &amem[ACTSIZE]; p++) {
+		if(*p)
+			continue;
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			if(*s)
+				goto nextgp;
+			if(s > maxa)
+				if((maxa = s) > &amem[ACTSIZE])
+					error("a array overflow");
+		}
+		/* we have found amem spot */
+		*p = *q2;
+		if(p > maxa)
+			if((maxa = p) > &amem[ACTSIZE])
+				error("a array overflow");
+		for(r = q1; r < q2; r += 2) {
+			s = p + *r + 1;
+			*s = r[1];
+		}
+		pgo[i] = p-amem;
+		if(adb > 1)
+			Bprint(ftable, "Nonterminal %d, entry at %d\n", i, pgo[i]);
+		return;
+
+	nextgp:;
+	}
+	error("cannot place goto %d\n", i);
+}
+
+void
+stin(int i)
+{
+	int *r, *s, n, flag, j, *q1, *q2;
+
+	tystate[i] = 0;
+
+	/* enter state i into the amem array */
+	q2 = mem0+temp1[i+1];
+	q1 = mem0+temp1[i];
+	/* find an acceptable place */
+	for(n = -maxoff; n < ACTSIZE; n++) {
+		flag = 0;
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r + n + amem) < amem)
+				goto nextn;
+			if(*s == 0)
+				flag++;
+			else
+				if(*s != r[1])
+					goto nextn;
+		}
+
+		/* check that the position equals another only if the states are identical */
+		for(j=0; j<nstate; j++) {
+			if(indgo[j] == n) {
+
+				/* we have some disagreement */
+				if(flag)
+					goto nextn;
+				if(temp1[j+1]+temp1[i] == temp1[j]+temp1[i+1]) {
+
+					/* states are equal */
+					indgo[i] = n;
+					if(adb > 1)
+						Bprint(ftable,
+						"State %d: entry at %d equals state %d\n",
+						i, n, j);
+					return;
+				}
+
+				/* we have some disagreement */
+				goto nextn;
+			}
+		}
+
+		for(r = q1; r < q2; r += 2) {
+			if((s = *r+n+amem) >= &amem[ACTSIZE])
+				error("out of space in optimizer a array");
+			if(s > maxa)
+				maxa = s;
+			if(*s != 0 && *s != r[1])
+				error("clobber of a array, pos'n %d, by %d", s-amem, r[1]);
+			*s = r[1];
+		}
+		indgo[i] = n;
+		if(adb > 1)
+			Bprint(ftable, "State %d: entry at %d\n", i, indgo[i]);
+		return;
+	nextn:;
+	}
+	error("Error; failure to place state %d\n", i);
+}
+
+/*
+ * finds the next i
+ */
+int
+nxti(void)
+{
+	int i, max, maxi;
+
+	max = 0;
+	maxi = 0;
+	for(i = 1; i <= nnonter; i++)
+		if(ggreed[i] >= max) {
+			max = ggreed[i];
+			maxi = -i;
+		}
+	for(i = 0; i < nstate; ++i)
+		if(tystate[i] >= max) {
+			max = tystate[i];
+			maxi = i;
+		}
+	if(nxdb)
+		Bprint(ftable, "nxti = %d, max = %d\n", maxi, max);
+	if(max == 0)
+		return NOMORE;
+	return maxi;
+}
+
+/*
+ * write summary
+ */
+void
+osummary(void)
+{
+
+	int i, *p;
+
+	if(foutput == 0)
+		return;
+	i = 0;
+	for(p = maxa; p >= amem; p--)
+		if(*p == 0)
+			i++;
+
+	Bprint(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
+		(int)(pmem-mem0+1), MEMSIZE, (int)(maxa-amem+1), ACTSIZE);
+	Bprint(foutput, "%d table entries, %d zero\n", (int)(maxa-amem+1), i);
+	Bprint(foutput, "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);
+}
+
+/*
+ * this version is for C
+ * write out the optimized parser
+ */
+void
+aoutput(void)
+{
+	Bprint(ftable, "#define\tYYLAST\t%d\n", (int)(maxa-amem+1));
+	arout("yyact", amem, (maxa-amem)+1);
+	arout("yypact", indgo, nstate);
+	arout("yypgo", pgo, nnonter+1);
+}
+
+void
+arout(char *s, int *v, int n)
+{
+	int i;
+
+	Bprint(ftable, "short	%s[] =\n{", s);
+	for(i = 0; i < n;) {
+		if(i%10 == 0)
+			Bprint(ftable, "\n");
+		Bprint(ftable, "%4d", v[i]);
+		i++;
+		if(i == n)
+			Bprint(ftable, "\n};\n");
+		else
+			Bprint(ftable, ",");
+	}
+}
+
+/*
+ * read and convert an integer from the standard input
+ * return the terminating character
+ * blanks, tabs, and newlines are ignored
+ */
+int
+gtnm(void)
+{
+	int sign, val, c;
+
+	sign = 0;
+	val = 0;
+	while((c=Bgetrune(finput)) != Beof) {
+		if(isdigit(c)) {
+			val = val*10 + c-'0';
+			continue;
+		}
+		if(c == '-') {
+			sign = 1;
+			continue;
+		}
+		break;
+	}
+	if(sign)
+		val = -val;
+	*pmem++ = val;
+	if(pmem >= &mem0[MEMSIZE])
+		error("out of space");
+	return c;
+}