| #include "sam.h" | 
 |  | 
 | Rune	genbuf[BLOCKSIZE]; | 
 | int	io; | 
 | int	panicking; | 
 | int	rescuing; | 
 | String	genstr; | 
 | String	rhs; | 
 | String	curwd; | 
 | String	cmdstr; | 
 | Rune	empty[] = { 0 }; | 
 | char	*genc; | 
 | File	*curfile; | 
 | File	*flist; | 
 | File	*cmd; | 
 | jmp_buf	mainloop; | 
 | List	tempfile = { 'p' }; | 
 | int	quitok = TRUE; | 
 | int	downloaded; | 
 | int	dflag; | 
 | int	Rflag; | 
 | char	*machine; | 
 | char	*home; | 
 | int	bpipeok; | 
 | int	termlocked; | 
 | char	*samterm = SAMTERM; | 
 | char	*rsamname = RSAM; | 
 | File	*lastfile; | 
 | Disk	*disk; | 
 | long	seq; | 
 |  | 
 | char *winsize; | 
 |  | 
 | Rune	baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'}; | 
 |  | 
 | void	usage(void); | 
 |  | 
 | extern int notify(void(*)(void*,char*)); | 
 |  | 
 | void | 
 | main(int _argc, char **_argv) | 
 | { | 
 | 	volatile int i, argc; | 
 | 	char **volatile argv; | 
 | 	String *t; | 
 | 	char *termargs[10], **ap; | 
 | 	 | 
 | 	argc = _argc; | 
 | 	argv = _argv; | 
 | 	ap = termargs; | 
 | 	*ap++ = "samterm"; | 
 | 	ARGBEGIN{ | 
 | 	case 'd': | 
 | 		dflag++; | 
 | 		break; | 
 | 	case 'r': | 
 | 		machine = EARGF(usage()); | 
 | 		break; | 
 | 	case 'R': | 
 | 		Rflag++; | 
 | 		break; | 
 | 	case 't': | 
 | 		samterm = EARGF(usage()); | 
 | 		break; | 
 | 	case 's': | 
 | 		rsamname = EARGF(usage()); | 
 | 		break; | 
 | 	default: | 
 | 		dprint("sam: unknown flag %c\n", ARGC()); | 
 | 		usage(); | 
 | 	/* options for samterm */ | 
 | 	case 'a': | 
 | 		*ap++ = "-a"; | 
 | 		break; | 
 | 	case 'W': | 
 | 		*ap++ = "-W"; | 
 | 		*ap++ = EARGF(usage()); | 
 | 		break; | 
 | 	}ARGEND | 
 | 	*ap = nil; | 
 |  | 
 | 	Strinit(&cmdstr); | 
 | 	Strinit0(&lastpat); | 
 | 	Strinit0(&lastregexp); | 
 | 	Strinit0(&genstr); | 
 | 	Strinit0(&rhs); | 
 | 	Strinit0(&curwd); | 
 | 	Strinit0(&plan9cmd); | 
 | 	home = getenv(HOME); | 
 | 	disk = diskinit(); | 
 | 	if(home == 0) | 
 | 		home = "/"; | 
 | 	if(!dflag) | 
 | 		startup(machine, Rflag, termargs, (char**)argv); | 
 | 	notify(notifyf); | 
 | 	getcurwd(); | 
 | 	if(argc>0){ | 
 | 		for(i=0; i<argc; i++){ | 
 | 			if(!setjmp(mainloop)){ | 
 | 				t = tmpcstr(argv[i]); | 
 | 				Straddc(t, '\0'); | 
 | 				Strduplstr(&genstr, t); | 
 | 				freetmpstr(t); | 
 | 				fixname(&genstr); | 
 | 				logsetname(newfile(), &genstr); | 
 | 			} | 
 | 		} | 
 | 	}else if(!downloaded) | 
 | 		newfile(); | 
 | 	seq++; | 
 | 	if(file.nused) | 
 | 		current(file.filepptr[0]); | 
 | 	setjmp(mainloop); | 
 | 	cmdloop(); | 
 | 	trytoquit();	/* if we already q'ed, quitok will be TRUE */ | 
 | 	exits(0); | 
 | } | 
 |  | 
 | void | 
 | usage(void) | 
 | { | 
 | 	dprint("usage: sam [-d] [-t samterm] [-s sam name] [-r machine] [file ...]\n"); | 
 | 	exits("usage"); | 
 | } | 
 |  | 
 | void | 
 | rescue(void) | 
 | { | 
 | 	int i, nblank = 0; | 
 | 	File *f; | 
 | 	char *c; | 
 | 	char buf[256]; | 
 | 	char *root; | 
 |  | 
 | 	if(rescuing++) | 
 | 		return; | 
 | 	io = -1; | 
 | 	for(i=0; i<file.nused; i++){ | 
 | 		f = file.filepptr[i]; | 
 | 		if(f==cmd || f->b.nc==0 || !fileisdirty(f)) | 
 | 			continue; | 
 | 		if(io == -1){ | 
 | 			sprint(buf, "%s/sam.save", home); | 
 | 			io = create(buf, 1, 0777); | 
 | 			if(io<0) | 
 | 				return; | 
 | 		} | 
 | 		if(f->name.s[0]){ | 
 | 			c = Strtoc(&f->name); | 
 | 			strncpy(buf, c, sizeof buf-1); | 
 | 			buf[sizeof buf-1] = 0; | 
 | 			free(c); | 
 | 		}else | 
 | 			sprint(buf, "nameless.%d", nblank++); | 
 | 		root = getenv("PLAN9"); | 
 | 		if(root == nil) | 
 | 			root = "/usr/local/plan9"; | 
 | 		fprint(io, "#!/bin/sh\n%s/bin/samsave '%s' $* <<'---%s'\n", root, buf, buf); | 
 | 		addr.r.p1 = 0, addr.r.p2 = f->b.nc; | 
 | 		writeio(f); | 
 | 		fprint(io, "\n---%s\n", (char *)buf); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | panic(char *s) | 
 | { | 
 | 	int wasd; | 
 |  | 
 | 	if(!panicking++ && !setjmp(mainloop)){ | 
 | 		wasd = downloaded; | 
 | 		downloaded = 0; | 
 | 		dprint("sam: panic: %s: %r\n", s); | 
 | 		if(wasd) | 
 | 			fprint(2, "sam: panic: %s: %r\n", s); | 
 | 		rescue(); | 
 | 		abort(); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | hiccough(char *s) | 
 | { | 
 | 	File *f; | 
 | 	int i; | 
 |  | 
 | 	if(rescuing) | 
 | 		exits("rescue"); | 
 | 	if(s) | 
 | 		dprint("%s\n", s); | 
 | 	resetcmd(); | 
 | 	resetxec(); | 
 | 	resetsys(); | 
 | 	if(io > 0) | 
 | 		close(io); | 
 |  | 
 | 	/* | 
 | 	 * back out any logged changes & restore old sequences | 
 | 	 */ | 
 | 	for(i=0; i<file.nused; i++){ | 
 | 		f = file.filepptr[i]; | 
 | 		if(f==cmd) | 
 | 			continue; | 
 | 		if(f->seq==seq){ | 
 | 			bufdelete(&f->epsilon, 0, f->epsilon.nc); | 
 | 			f->seq = f->prevseq; | 
 | 			f->dot.r = f->prevdot; | 
 | 			f->mark = f->prevmark; | 
 | 			state(f, f->prevmod ? Dirty: Clean); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	update(); | 
 | 	if (curfile) { | 
 | 		if (curfile->unread) | 
 | 			curfile->unread = FALSE; | 
 | 		else if (downloaded) | 
 | 			outTs(Hcurrent, curfile->tag); | 
 | 	} | 
 | 	longjmp(mainloop, 1); | 
 | } | 
 |  | 
 | void | 
 | intr(void) | 
 | { | 
 | 	error(Eintr); | 
 | } | 
 |  | 
 | void | 
 | trytoclose(File *f) | 
 | { | 
 | 	char *t; | 
 | 	char buf[256]; | 
 |  | 
 | 	if(f == cmd)	/* possible? */ | 
 | 		return; | 
 | 	if(f->deleted) | 
 | 		return; | 
 | 	if(fileisdirty(f) && !f->closeok){ | 
 | 		f->closeok = TRUE; | 
 | 		if(f->name.s[0]){ | 
 | 			t = Strtoc(&f->name); | 
 | 			strncpy(buf, t, sizeof buf-1); | 
 | 			free(t); | 
 | 		}else | 
 | 			strcpy(buf, "nameless file"); | 
 | 		error_s(Emodified, buf); | 
 | 	} | 
 | 	f->deleted = TRUE; | 
 | } | 
 |  | 
 | void | 
 | trytoquit(void) | 
 | { | 
 | 	int c; | 
 | 	File *f; | 
 |  | 
 | 	if(!quitok){ | 
 | 		for(c = 0; c<file.nused; c++){ | 
 | 			f = file.filepptr[c]; | 
 | 			if(f!=cmd && fileisdirty(f)){ | 
 | 				quitok = TRUE; | 
 | 				eof = FALSE; | 
 | 				error(Echanges); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | load(File *f) | 
 | { | 
 | 	Address saveaddr; | 
 |  | 
 | 	Strduplstr(&genstr, &f->name); | 
 | 	filename(f); | 
 | 	if(f->name.s[0]){ | 
 | 		saveaddr = addr; | 
 | 		edit(f, 'I'); | 
 | 		addr = saveaddr; | 
 | 	}else{ | 
 | 		f->unread = 0; | 
 | 		f->cleanseq = f->seq; | 
 | 	} | 
 |  | 
 | 	fileupdate(f, TRUE, TRUE); | 
 | } | 
 |  | 
 | void | 
 | cmdupdate(void) | 
 | { | 
 | 	if(cmd && cmd->seq!=0){ | 
 | 		fileupdate(cmd, FALSE, downloaded); | 
 | 		cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->b.nc; | 
 | 		telldot(cmd); | 
 | 	} | 
 | } | 
 |  | 
 | void | 
 | delete(File *f) | 
 | { | 
 | 	if(downloaded && f->rasp) | 
 | 		outTs(Hclose, f->tag); | 
 | 	delfile(f); | 
 | 	if(f == curfile) | 
 | 		current(0); | 
 | } | 
 |  | 
 | void | 
 | update(void) | 
 | { | 
 | 	int i, anymod; | 
 | 	File *f; | 
 |  | 
 | 	settempfile(); | 
 | 	for(anymod = i=0; i<tempfile.nused; i++){ | 
 | 		f = tempfile.filepptr[i]; | 
 | 		if(f==cmd)	/* cmd gets done in main() */ | 
 | 			continue; | 
 | 		if(f->deleted) { | 
 | 			delete(f); | 
 | 			continue; | 
 | 		} | 
 | 		if(f->seq==seq && fileupdate(f, FALSE, downloaded)) | 
 | 			anymod++; | 
 | 		if(f->rasp) | 
 | 			telldot(f); | 
 | 	} | 
 | 	if(anymod) | 
 | 		seq++; | 
 | } | 
 |  | 
 | File * | 
 | current(File *f) | 
 | { | 
 | 	return curfile = f; | 
 | } | 
 |  | 
 | void | 
 | edit(File *f, int cmd) | 
 | { | 
 | 	int empty = TRUE; | 
 | 	Posn p; | 
 | 	int nulls; | 
 |  | 
 | 	if(cmd == 'r') | 
 | 		logdelete(f, addr.r.p1, addr.r.p2); | 
 | 	if(cmd=='e' || cmd=='I'){ | 
 | 		logdelete(f, (Posn)0, f->b.nc); | 
 | 		addr.r.p2 = f->b.nc; | 
 | 	}else if(f->b.nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0)) | 
 | 		empty = FALSE; | 
 | 	if((io = open(genc, OREAD))<0) { | 
 | 		if (curfile && curfile->unread) | 
 | 			curfile->unread = FALSE; | 
 | 		error_r(Eopen, genc); | 
 | 	} | 
 | 	p = readio(f, &nulls, empty, TRUE); | 
 | 	closeio((cmd=='e' || cmd=='I')? -1 : p); | 
 | 	if(cmd == 'r') | 
 | 		f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p; | 
 | 	else | 
 | 		f->ndot.r.p1 = f->ndot.r.p2 = 0; | 
 | 	f->closeok = empty; | 
 | 	if (quitok) | 
 | 		quitok = empty; | 
 | 	else | 
 | 		quitok = FALSE; | 
 | 	state(f, empty && !nulls? Clean : Dirty); | 
 | 	if(empty && !nulls) | 
 | 		f->cleanseq = f->seq; | 
 | 	if(cmd == 'e') | 
 | 		filename(f); | 
 | } | 
 |  | 
 | int | 
 | getname(File *f, String *s, int save) | 
 | { | 
 | 	int c, i; | 
 |  | 
 | 	Strzero(&genstr); | 
 | 	if(genc){ | 
 | 		free(genc); | 
 | 		genc = 0; | 
 | 	} | 
 | 	if(s==0 || (c = s->s[0])==0){		/* no name provided */ | 
 | 		if(f) | 
 | 			Strduplstr(&genstr, &f->name); | 
 | 		goto Return; | 
 | 	} | 
 | 	if(c!=' ' && c!='\t') | 
 | 		error(Eblank); | 
 | 	for(i=0; (c=s->s[i])==' ' || c=='\t'; i++) | 
 | 		; | 
 | 	while(s->s[i] > ' ') | 
 | 		Straddc(&genstr, s->s[i++]); | 
 | 	if(s->s[i]) | 
 | 		error(Enewline); | 
 | 	fixname(&genstr); | 
 | 	if(f && (save || f->name.s[0]==0)){ | 
 | 		logsetname(f, &genstr); | 
 | 		if(Strcmp(&f->name, &genstr)){ | 
 | 			quitok = f->closeok = FALSE; | 
 | 			f->qidpath = 0; | 
 | 			f->mtime = 0; | 
 | 			state(f, Dirty); /* if it's 'e', fix later */ | 
 | 		} | 
 | 	} | 
 |     Return: | 
 | 	genc = Strtoc(&genstr); | 
 | 	i = genstr.n; | 
 | 	if(i && genstr.s[i-1]==0) | 
 | 		i--; | 
 | 	return i;	/* strlen(name) */ | 
 | } | 
 |  | 
 | void | 
 | filename(File *f) | 
 | { | 
 | 	if(genc) | 
 | 		free(genc); | 
 | 	genc = Strtoc(&genstr); | 
 | 	dprint("%c%c%c %s\n", " '"[f->mod], | 
 | 		"-+"[f->rasp!=0], " ."[f==curfile], genc); | 
 | } | 
 |  | 
 | void | 
 | undostep(File *f, int isundo) | 
 | { | 
 | 	uint p1, p2; | 
 | 	int mod; | 
 |  | 
 | 	mod = f->mod; | 
 | 	fileundo(f, isundo, 1, &p1, &p2, TRUE); | 
 | 	f->ndot = f->dot; | 
 | 	if(f->mod){ | 
 | 		f->closeok = 0; | 
 | 		quitok = 0; | 
 | 	}else | 
 | 		f->closeok = 1; | 
 |  | 
 | 	if(f->mod != mod){ | 
 | 		f->mod = mod; | 
 | 		if(mod) | 
 | 			mod = Clean; | 
 | 		else | 
 | 			mod = Dirty; | 
 | 		state(f, mod); | 
 | 	} | 
 | } | 
 |  | 
 | int | 
 | undo(int isundo) | 
 | { | 
 | 	File *f; | 
 | 	int i; | 
 | 	Mod max; | 
 |  | 
 | 	max = undoseq(curfile, isundo); | 
 | 	if(max == 0) | 
 | 		return 0; | 
 | 	settempfile(); | 
 | 	for(i = 0; i<tempfile.nused; i++){ | 
 | 		f = tempfile.filepptr[i]; | 
 | 		if(f!=cmd && undoseq(f, isundo)==max) | 
 | 			undostep(f, isundo); | 
 | 	} | 
 | 	return 1; | 
 | } | 
 |  | 
 | int | 
 | readcmd(String *s) | 
 | { | 
 | 	int retcode; | 
 |  | 
 | 	if(flist != 0) | 
 | 		fileclose(flist); | 
 | 	flist = fileopen(); | 
 |  | 
 | 	addr.r.p1 = 0, addr.r.p2 = flist->b.nc; | 
 | 	retcode = plan9(flist, '<', s, FALSE); | 
 | 	fileupdate(flist, FALSE, FALSE); | 
 | 	flist->seq = 0; | 
 | 	if (flist->b.nc > BLOCKSIZE) | 
 | 		error(Etoolong); | 
 | 	Strzero(&genstr); | 
 | 	Strinsure(&genstr, flist->b.nc); | 
 | 	bufread(&flist->b, (Posn)0, genbuf, flist->b.nc); | 
 | 	memmove(genstr.s, genbuf, flist->b.nc*RUNESIZE); | 
 | 	genstr.n = flist->b.nc; | 
 | 	Straddc(&genstr, '\0'); | 
 | 	return retcode; | 
 | } | 
 |  | 
 | void | 
 | getcurwd(void) | 
 | { | 
 | 	String *t; | 
 | 	char buf[256]; | 
 |  | 
 | 	buf[0] = 0; | 
 | 	getwd(buf, sizeof(buf)); | 
 | 	t = tmpcstr(buf); | 
 | 	Strduplstr(&curwd, t); | 
 | 	freetmpstr(t); | 
 | 	if(curwd.n == 0) | 
 | 		warn(Wpwd); | 
 | 	else if(curwd.s[curwd.n-1] != '/') | 
 | 		Straddc(&curwd, '/'); | 
 | } | 
 |  | 
 | void | 
 | cd(String *str) | 
 | { | 
 | 	int i, fd; | 
 | 	char *s; | 
 | 	File *f; | 
 | 	String owd; | 
 |  | 
 | 	getcurwd(); | 
 | 	if(getname((File *)0, str, FALSE)) | 
 | 		s = genc; | 
 | 	else | 
 | 		s = home; | 
 | 	if(chdir(s)) | 
 | 		syserror("chdir"); | 
 | 	fd = open("/dev/wdir", OWRITE); | 
 | 	if(fd > 0) | 
 | 		write(fd, s, strlen(s)); | 
 | 	dprint("!\n"); | 
 | 	Strinit(&owd); | 
 | 	Strduplstr(&owd, &curwd); | 
 | 	getcurwd(); | 
 | 	settempfile(); | 
 | 	/* | 
 | 	 * Two passes so that if we have open | 
 | 	 * /a/foo.c and /b/foo.c and cd from /b to /a, | 
 | 	 * we don't ever have two foo.c simultaneously. | 
 | 	 */ | 
 | 	for(i=0; i<tempfile.nused; i++){ | 
 | 		f = tempfile.filepptr[i]; | 
 | 		if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){ | 
 | 			Strinsert(&f->name, &owd, (Posn)0); | 
 | 			fixname(&f->name); | 
 | 			sortname(f); | 
 | 		} | 
 | 	} | 
 | 	for(i=0; i<tempfile.nused; i++){ | 
 | 		f = tempfile.filepptr[i]; | 
 | 		if(f != cmd && Strispre(&curwd, &f->name)){ | 
 | 			fixname(&f->name); | 
 | 			sortname(f); | 
 | 		} | 
 | 	} | 
 | 	Strclose(&owd); | 
 | } | 
 |  | 
 | int | 
 | loadflist(String *s) | 
 | { | 
 | 	int c, i; | 
 |  | 
 | 	c = s->s[0]; | 
 | 	for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++) | 
 | 		; | 
 | 	if((c==' ' || c=='\t') && s->s[i]!='\n'){ | 
 | 		if(s->s[i]=='<'){ | 
 | 			Strdelete(s, 0L, (long)i+1); | 
 | 			readcmd(s); | 
 | 		}else{ | 
 | 			Strzero(&genstr); | 
 | 			while((c = s->s[i++]) && c!='\n') | 
 | 				Straddc(&genstr, c); | 
 | 			Straddc(&genstr, '\0'); | 
 | 		} | 
 | 	}else{ | 
 | 		if(c != '\n') | 
 | 			error(Eblank); | 
 | 		Strdupl(&genstr, empty); | 
 | 	} | 
 | 	if(genc) | 
 | 		free(genc); | 
 | 	genc = Strtoc(&genstr); | 
 | 	return genstr.s[0]; | 
 | } | 
 |  | 
 | File * | 
 | readflist(int readall, int delete) | 
 | { | 
 | 	Posn i; | 
 | 	int c; | 
 | 	File *f; | 
 | 	String t; | 
 |  | 
 | 	Strinit(&t); | 
 | 	for(i=0,f=0; f==0 || readall || delete; i++){	/* ++ skips blank */ | 
 | 		Strdelete(&genstr, (Posn)0, i); | 
 | 		for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++) | 
 | 			; | 
 | 		if(i >= genstr.n) | 
 | 			break; | 
 | 		Strdelete(&genstr, (Posn)0, i); | 
 | 		for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++) | 
 | 			; | 
 |  | 
 | 		if(i == 0) | 
 | 			break; | 
 | 		genstr.s[i] = 0; | 
 | 		Strduplstr(&t, tmprstr(genstr.s, i+1)); | 
 | 		fixname(&t); | 
 | 		f = lookfile(&t); | 
 | 		if(delete){ | 
 | 			if(f == 0) | 
 | 				warn_S(Wfile, &t); | 
 | 			else | 
 | 				trytoclose(f); | 
 | 		}else if(f==0 && readall) | 
 | 			logsetname(f = newfile(), &t); | 
 | 	} | 
 | 	Strclose(&t); | 
 | 	return f; | 
 | } | 
 |  | 
 | File * | 
 | tofile(String *s) | 
 | { | 
 | 	File *f; | 
 |  | 
 | 	if(s->s[0] != ' ') | 
 | 		error(Eblank); | 
 | 	if(loadflist(s) == 0){ | 
 | 		f = lookfile(&genstr);	/* empty string ==> nameless file */ | 
 | 		if(f == 0) | 
 | 			error_s(Emenu, genc); | 
 | 	}else if((f=readflist(FALSE, FALSE)) == 0) | 
 | 		error_s(Emenu, genc); | 
 | 	return current(f); | 
 | } | 
 |  | 
 | File * | 
 | getfile(String *s) | 
 | { | 
 | 	File *f; | 
 |  | 
 | 	if(loadflist(s) == 0) | 
 | 		logsetname(f = newfile(), &genstr); | 
 | 	else if((f=readflist(TRUE, FALSE)) == 0) | 
 | 		error(Eblank); | 
 | 	return current(f); | 
 | } | 
 |  | 
 | void | 
 | closefiles(File *f, String *s) | 
 | { | 
 | 	if(s->s[0] == 0){ | 
 | 		if(f == 0) | 
 | 			error(Enofile); | 
 | 		trytoclose(f); | 
 | 		return; | 
 | 	} | 
 | 	if(s->s[0] != ' ') | 
 | 		error(Eblank); | 
 | 	if(loadflist(s) == 0) | 
 | 		error(Enewline); | 
 | 	readflist(FALSE, TRUE); | 
 | } | 
 |  | 
 | void | 
 | copy(File *f, Address addr2) | 
 | { | 
 | 	Posn p; | 
 | 	int ni; | 
 | 	for(p=addr.r.p1; p<addr.r.p2; p+=ni){ | 
 | 		ni = addr.r.p2-p; | 
 | 		if(ni > BLOCKSIZE) | 
 | 			ni = BLOCKSIZE; | 
 | 		bufread(&f->b, p, genbuf, ni); | 
 | 		loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni); | 
 | 	} | 
 | 	addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1); | 
 | 	addr2.f->ndot.r.p1 = addr2.r.p2; | 
 | } | 
 |  | 
 | void | 
 | move(File *f, Address addr2) | 
 | { | 
 | 	if(addr.r.p2 <= addr2.r.p2){ | 
 | 		logdelete(f, addr.r.p1, addr.r.p2); | 
 | 		copy(f, addr2); | 
 | 	}else if(addr.r.p1 >= addr2.r.p2){ | 
 | 		copy(f, addr2); | 
 | 		logdelete(f, addr.r.p1, addr.r.p2); | 
 | 	}else | 
 | 		error(Eoverlap); | 
 | } | 
 |  | 
 | Posn | 
 | nlcount(File *f, Posn p0, Posn p1) | 
 | { | 
 | 	Posn nl = 0; | 
 |  | 
 | 	while(p0 < p1) | 
 | 		if(filereadc(f, p0++)=='\n') | 
 | 			nl++; | 
 | 	return nl; | 
 | } | 
 |  | 
 | void | 
 | printposn(File *f, int charsonly) | 
 | { | 
 | 	Posn l1, l2; | 
 |  | 
 | 	if(!charsonly){ | 
 | 		l1 = 1+nlcount(f, (Posn)0, addr.r.p1); | 
 | 		l2 = l1+nlcount(f, addr.r.p1, addr.r.p2); | 
 | 		/* check if addr ends with '\n' */ | 
 | 		if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n') | 
 | 			--l2; | 
 | 		dprint("%lud", l1); | 
 | 		if(l2 != l1) | 
 | 			dprint(",%lud", l2); | 
 | 		dprint("; "); | 
 | 	} | 
 | 	dprint("#%lud", addr.r.p1); | 
 | 	if(addr.r.p2 != addr.r.p1) | 
 | 		dprint(",#%lud", addr.r.p2); | 
 | 	dprint("\n"); | 
 | } | 
 |  | 
 | void | 
 | settempfile(void) | 
 | { | 
 | 	if(tempfile.nalloc < file.nused){ | 
 | 		if(tempfile.filepptr) | 
 | 			free(tempfile.filepptr); | 
 | 		tempfile.filepptr = emalloc(sizeof(File*)*file.nused); | 
 | 		tempfile.nalloc = file.nused; | 
 | 	} | 
 | 	memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused); | 
 | 	tempfile.nused = file.nused; | 
 | } |