Initial revision
diff --git a/src/cmd/sam/io.c b/src/cmd/sam/io.c
new file mode 100644
index 0000000..236090a
--- /dev/null
+++ b/src/cmd/sam/io.c
@@ -0,0 +1,262 @@
+#include "sam.h"
+
+#define	NSYSFILE	3
+#define	NOFILE		128
+
+void
+checkqid(File *f)
+{
+	int i, w;
+	File *g;
+
+	w = whichmenu(f);
+	for(i=1; i<file.nused; i++){
+		g = file.filepptr[i];
+		if(w == i)
+			continue;
+		if(f->dev==g->dev && f->qidpath==g->qidpath)
+			warn_SS(Wdupfile, &f->name, &g->name);
+	}
+}
+
+void
+writef(File *f)
+{
+	Posn n;
+	char *name;
+	int i, samename, newfile;
+	ulong dev;
+	uvlong qid;
+	long mtime, appendonly, length;
+
+	newfile = 0;
+	samename = Strcmp(&genstr, &f->name) == 0;
+	name = Strtoc(&f->name);
+	i = statfile(name, &dev, &qid, &mtime, 0, 0);
+	if(i == -1)
+		newfile++;
+	else if(samename &&
+	        (f->dev!=dev || f->qidpath!=qid || f->mtime<mtime)){
+		f->dev = dev;
+		f->qidpath = qid;
+		f->mtime = mtime;
+		warn_S(Wdate, &genstr);
+		return;
+	}
+	if(genc)
+		free(genc);
+	genc = Strtoc(&genstr);
+	if((io=create(genc, 1, 0666L)) < 0)
+		error_r(Ecreate, genc);
+	dprint("%s: ", genc);
+	if(statfd(io, 0, 0, 0, &length, &appendonly) > 0 && appendonly && length>0)
+		error(Eappend);
+	n = writeio(f);
+	if(f->name.s[0]==0 || samename){
+		if(addr.r.p1==0 && addr.r.p2==f->_.nc)
+			f->cleanseq = f->seq;
+		state(f, f->cleanseq==f->seq? Clean : Dirty);
+	}
+	if(newfile)
+		dprint("(new file) ");
+	if(addr.r.p2>0 && filereadc(f, addr.r.p2-1)!='\n')
+		warn(Wnotnewline);
+	closeio(n);
+	if(f->name.s[0]==0 || samename){
+		if(statfile(name, &dev, &qid, &mtime, 0, 0) > 0){
+			f->dev = dev;
+			f->qidpath = qid;
+			f->mtime = mtime;
+			checkqid(f);
+		}
+	}
+}
+
+Posn
+readio(File *f, int *nulls, int setdate, int toterm)
+{
+	int n, b, w;
+	Rune *r;
+	Posn nt;
+	Posn p = addr.r.p2;
+	ulong dev;
+	uvlong qid;
+	long mtime;
+	char buf[BLOCKSIZE+1], *s;
+
+	*nulls = FALSE;
+	b = 0;
+	if(f->unread){
+		nt = bufload(f, 0, io, nulls);
+		if(toterm)
+			raspload(f);
+	}else
+		for(nt = 0; (n = read(io, buf+b, BLOCKSIZE-b))>0; nt+=(r-genbuf)){
+			n += b;
+			b = 0;
+			r = genbuf;
+			s = buf;
+			while(n > 0){
+				if((*r = *(uchar*)s) < Runeself){
+					if(*r)
+						r++;
+					else
+						*nulls = TRUE;
+					--n;
+					s++;
+					continue;
+				}
+				if(fullrune(s, n)){
+					w = chartorune(r, s);
+					if(*r)
+						r++;
+					else
+						*nulls = TRUE;
+					n -= w;
+					s += w;
+					continue;
+				}
+				b = n;
+				memmove(buf, s, b);
+				break;
+			}
+			loginsert(f, p, genbuf, r-genbuf);
+		}
+	if(b)
+		*nulls = TRUE;
+	if(*nulls)
+		warn(Wnulls);
+	if(setdate){
+		if(statfd(io, &dev, &qid, &mtime, 0, 0) > 0){
+			f->dev = dev;
+			f->qidpath = qid;
+			f->mtime = mtime;
+			checkqid(f);
+		}
+	}
+	return nt;
+}
+
+Posn
+writeio(File *f)
+{
+	int m, n;
+	Posn p = addr.r.p1;
+	char *c;
+
+	while(p < addr.r.p2){
+		if(addr.r.p2-p>BLOCKSIZE)
+			n = BLOCKSIZE;
+		else
+			n = addr.r.p2-p;
+		bufread(f, p, genbuf, n);
+		c = Strtoc(tmprstr(genbuf, n));
+		m = strlen(c);
+		if(Write(io, c, m) != m){
+			free(c);
+			if(p > 0)
+				p += n;
+			break;
+		}
+		free(c);
+		p += n;
+	}
+	return p-addr.r.p1;
+}
+void
+closeio(Posn p)
+{
+	close(io);
+	io = 0;
+	if(p >= 0)
+		dprint("#%lud\n", p);
+}
+
+int	remotefd0 = 0;
+int	remotefd1 = 1;
+
+void
+bootterm(char *machine, char **argv, char **end)
+{
+	int ph2t[2], pt2h[2];
+
+	if(machine){
+		dup(remotefd0, 0);
+		dup(remotefd1, 1);
+		close(remotefd0);
+		close(remotefd1);
+		argv[0] = "samterm";
+		*end = 0;
+		exec(samterm, argv);
+		fprint(2, "can't exec: ");
+		perror(samterm);
+		_exits("damn");
+	}
+	if(pipe(ph2t)==-1 || pipe(pt2h)==-1)
+		panic("pipe");
+	switch(fork()){
+	case 0:
+		dup(ph2t[0], 0);
+		dup(pt2h[1], 1);
+		close(ph2t[0]);
+		close(ph2t[1]);
+		close(pt2h[0]);
+		close(pt2h[1]);
+		argv[0] = "samterm";
+		*end = 0;
+		exec(samterm, argv);
+		fprint(2, "can't exec: ");
+		perror(samterm);
+		_exits("damn");
+	case -1:
+		panic("can't fork samterm");
+	}
+	dup(pt2h[0], 0);
+	dup(ph2t[1], 1);
+	close(ph2t[0]);
+	close(ph2t[1]);
+	close(pt2h[0]);
+	close(pt2h[1]);
+}
+
+void
+connectto(char *machine)
+{
+	int p1[2], p2[2];
+
+	if(pipe(p1)<0 || pipe(p2)<0){
+		dprint("can't pipe\n");
+		exits("pipe");
+	}
+	remotefd0 = p1[0];
+	remotefd1 = p2[1];
+	switch(fork()){
+	case 0:
+		dup(p2[0], 0);
+		dup(p1[1], 1);
+		close(p1[0]);
+		close(p1[1]);
+		close(p2[0]);
+		close(p2[1]);
+		execl(RXPATH, RX, machine, rsamname, "-R", (char*)0);
+		dprint("can't exec %s\n", RXPATH);
+		exits("exec");
+
+	case -1:
+		dprint("can't fork\n");
+		exits("fork");
+	}
+	close(p1[1]);
+	close(p2[0]);
+}
+
+void
+startup(char *machine, int Rflag, char **argv, char **end)
+{
+	if(machine)
+		connectto(machine);
+	if(!Rflag)
+		bootterm(machine, argv, end);
+	downloaded = 1;
+	outTs(Hversion, VERSION);
+}