xref: /plan9/sys/src/cmd/acme/ecmd.c (revision de7131e12e81943b01c32c3c862316fe2448353d)
159cc4ca5SDavid du Colombier #include <u.h>
259cc4ca5SDavid du Colombier #include <libc.h>
359cc4ca5SDavid du Colombier #include <draw.h>
459cc4ca5SDavid du Colombier #include <thread.h>
559cc4ca5SDavid du Colombier #include <cursor.h>
659cc4ca5SDavid du Colombier #include <mouse.h>
759cc4ca5SDavid du Colombier #include <keyboard.h>
859cc4ca5SDavid du Colombier #include <frame.h>
959cc4ca5SDavid du Colombier #include <fcall.h>
1059cc4ca5SDavid du Colombier #include <plumb.h>
1159cc4ca5SDavid du Colombier #include "dat.h"
1259cc4ca5SDavid du Colombier #include "edit.h"
1359cc4ca5SDavid du Colombier #include "fns.h"
1459cc4ca5SDavid du Colombier 
1559cc4ca5SDavid du Colombier int	Glooping;
1659cc4ca5SDavid du Colombier int	nest;
1759cc4ca5SDavid du Colombier char	Enoname[] = "no file name given";
1859cc4ca5SDavid du Colombier 
1959cc4ca5SDavid du Colombier Address	addr;
2059cc4ca5SDavid du Colombier File	*menu;
2159cc4ca5SDavid du Colombier Rangeset	sel;
2259cc4ca5SDavid du Colombier extern	Text*	curtext;
2359cc4ca5SDavid du Colombier Rune	*collection;
2459cc4ca5SDavid du Colombier int	ncollection;
2559cc4ca5SDavid du Colombier 
2659cc4ca5SDavid du Colombier int	append(File*, Cmd*, long);
2759cc4ca5SDavid du Colombier int	pdisplay(File*);
2859cc4ca5SDavid du Colombier void	pfilename(File*);
2959cc4ca5SDavid du Colombier void	looper(File*, Cmd*, int);
3059cc4ca5SDavid du Colombier void	filelooper(Cmd*, int);
3159cc4ca5SDavid du Colombier void	linelooper(File*, Cmd*);
3259cc4ca5SDavid du Colombier Address	lineaddr(long, Address, int);
3359cc4ca5SDavid du Colombier int	filematch(File*, String*);
3459cc4ca5SDavid du Colombier File	*tofile(String*);
3559cc4ca5SDavid du Colombier Rune*	cmdname(File *f, String *s, int);
3659cc4ca5SDavid du Colombier void	runpipe(Text*, int, Rune*, int, int);
3759cc4ca5SDavid du Colombier 
3859cc4ca5SDavid du Colombier void
clearcollection(void)3959cc4ca5SDavid du Colombier clearcollection(void)
4059cc4ca5SDavid du Colombier {
4159cc4ca5SDavid du Colombier 	free(collection);
4259cc4ca5SDavid du Colombier 	collection = nil;
4359cc4ca5SDavid du Colombier 	ncollection = 0;
4459cc4ca5SDavid du Colombier }
4559cc4ca5SDavid du Colombier 
4659cc4ca5SDavid du Colombier void
resetxec(void)4759cc4ca5SDavid du Colombier resetxec(void)
4859cc4ca5SDavid du Colombier {
4959cc4ca5SDavid du Colombier 	Glooping = nest = 0;
5059cc4ca5SDavid du Colombier 	clearcollection();
5159cc4ca5SDavid du Colombier }
5259cc4ca5SDavid du Colombier 
5359cc4ca5SDavid du Colombier void
mkaddr(Address * a,File * f)5459cc4ca5SDavid du Colombier mkaddr(Address *a, File *f)
5559cc4ca5SDavid du Colombier {
5659cc4ca5SDavid du Colombier 	a->r.q0 = f->curtext->q0;
5759cc4ca5SDavid du Colombier 	a->r.q1 = f->curtext->q1;
5859cc4ca5SDavid du Colombier 	a->f = f;
5959cc4ca5SDavid du Colombier }
6059cc4ca5SDavid du Colombier 
6159cc4ca5SDavid du Colombier int
cmdexec(Text * t,Cmd * cp)6259cc4ca5SDavid du Colombier cmdexec(Text *t, Cmd *cp)
6359cc4ca5SDavid du Colombier {
6459cc4ca5SDavid du Colombier 	int i;
6559cc4ca5SDavid du Colombier 	Addr *ap;
6659cc4ca5SDavid du Colombier 	File *f;
6759cc4ca5SDavid du Colombier 	Window *w;
6859cc4ca5SDavid du Colombier 	Address dot;
6959cc4ca5SDavid du Colombier 
7059cc4ca5SDavid du Colombier 	if(t == nil)
7159cc4ca5SDavid du Colombier 		w = nil;
7259cc4ca5SDavid du Colombier 	else
7359cc4ca5SDavid du Colombier 		w = t->w;
7459cc4ca5SDavid du Colombier 	if(w==nil && (cp->addr==0 || cp->addr->type!='"') &&
7559cc4ca5SDavid du Colombier 	    !utfrune("bBnqUXY!", cp->cmdc) &&
7659cc4ca5SDavid du Colombier 	    !(cp->cmdc=='D' && cp->text))
7759cc4ca5SDavid du Colombier 		editerror("no current window");
7859cc4ca5SDavid du Colombier 	i = cmdlookup(cp->cmdc);	/* will be -1 for '{' */
7959cc4ca5SDavid du Colombier 	f = nil;
8059cc4ca5SDavid du Colombier 	if(t && t->w){
8159cc4ca5SDavid du Colombier 		t = &t->w->body;
8259cc4ca5SDavid du Colombier 		f = t->file;
8359cc4ca5SDavid du Colombier 		f->curtext = t;
8459cc4ca5SDavid du Colombier 	}
8559cc4ca5SDavid du Colombier 	if(i>=0 && cmdtab[i].defaddr != aNo){
8659cc4ca5SDavid du Colombier 		if((ap=cp->addr)==0 && cp->cmdc!='\n'){
8759cc4ca5SDavid du Colombier 			cp->addr = ap = newaddr();
8859cc4ca5SDavid du Colombier 			ap->type = '.';
8959cc4ca5SDavid du Colombier 			if(cmdtab[i].defaddr == aAll)
9059cc4ca5SDavid du Colombier 				ap->type = '*';
9159cc4ca5SDavid du Colombier 		}else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){
9259cc4ca5SDavid du Colombier 			ap->next = newaddr();
9359cc4ca5SDavid du Colombier 			ap->next->type = '.';
9459cc4ca5SDavid du Colombier 			if(cmdtab[i].defaddr == aAll)
9559cc4ca5SDavid du Colombier 				ap->next->type = '*';
9659cc4ca5SDavid du Colombier 		}
9759cc4ca5SDavid du Colombier 		if(cp->addr){	/* may be false for '\n' (only) */
9859cc4ca5SDavid du Colombier 			static Address none = {0,0,nil};
9959cc4ca5SDavid du Colombier 			if(f){
10059cc4ca5SDavid du Colombier 				mkaddr(&dot, f);
10159cc4ca5SDavid du Colombier 				addr = cmdaddress(ap, dot, 0);
10259cc4ca5SDavid du Colombier 			}else	/* a " */
10359cc4ca5SDavid du Colombier 				addr = cmdaddress(ap, none, 0);
10459cc4ca5SDavid du Colombier 			f = addr.f;
10559cc4ca5SDavid du Colombier 			t = f->curtext;
10659cc4ca5SDavid du Colombier 		}
10759cc4ca5SDavid du Colombier 	}
10859cc4ca5SDavid du Colombier 	switch(cp->cmdc){
10959cc4ca5SDavid du Colombier 	case '{':
11059cc4ca5SDavid du Colombier 		mkaddr(&dot, f);
11159cc4ca5SDavid du Colombier 		if(cp->addr != nil)
11259cc4ca5SDavid du Colombier 			dot = cmdaddress(cp->addr, dot, 0);
11359cc4ca5SDavid du Colombier 		for(cp = cp->cmd; cp; cp = cp->next){
1146b6b9ac8SDavid du Colombier 			if(dot.r.q1 > t->file->nc)
1156b6b9ac8SDavid du Colombier 				editerror("dot extends past end of buffer during { command");
11659cc4ca5SDavid du Colombier 			t->q0 = dot.r.q0;
11759cc4ca5SDavid du Colombier 			t->q1 = dot.r.q1;
11859cc4ca5SDavid du Colombier 			cmdexec(t, cp);
11959cc4ca5SDavid du Colombier 		}
12059cc4ca5SDavid du Colombier 		break;
12159cc4ca5SDavid du Colombier 	default:
12259cc4ca5SDavid du Colombier 		if(i < 0)
12359cc4ca5SDavid du Colombier 			editerror("unknown command %c in cmdexec", cp->cmdc);
12459cc4ca5SDavid du Colombier 		i = (*cmdtab[i].fn)(t, cp);
12559cc4ca5SDavid du Colombier 		return i;
12659cc4ca5SDavid du Colombier 	}
12759cc4ca5SDavid du Colombier 	return 1;
12859cc4ca5SDavid du Colombier }
12959cc4ca5SDavid du Colombier 
13059cc4ca5SDavid du Colombier char*
edittext(Window * w,int q,Rune * r,int nr)131e288d156SDavid du Colombier edittext(Window *w, int q, Rune *r, int nr)
13259cc4ca5SDavid du Colombier {
133e288d156SDavid du Colombier 	File *f;
134e288d156SDavid du Colombier 
135e288d156SDavid du Colombier 	f = w->body.file;
13659cc4ca5SDavid du Colombier 	switch(editing){
13759cc4ca5SDavid du Colombier 	case Inactive:
13859cc4ca5SDavid du Colombier 		return "permission denied";
13959cc4ca5SDavid du Colombier 	case Inserting:
14059cc4ca5SDavid du Colombier 		eloginsert(f, q, r, nr);
14159cc4ca5SDavid du Colombier 		return nil;
14259cc4ca5SDavid du Colombier 	case Collecting:
14359cc4ca5SDavid du Colombier 		collection = runerealloc(collection, ncollection+nr+1);
14459cc4ca5SDavid du Colombier 		runemove(collection+ncollection, r, nr);
14559cc4ca5SDavid du Colombier 		ncollection += nr;
14659cc4ca5SDavid du Colombier 		collection[ncollection] = '\0';
14759cc4ca5SDavid du Colombier 		return nil;
14859cc4ca5SDavid du Colombier 	default:
14959cc4ca5SDavid du Colombier 		return "unknown state in edittext";
15059cc4ca5SDavid du Colombier 	}
15159cc4ca5SDavid du Colombier }
15259cc4ca5SDavid du Colombier 
15359cc4ca5SDavid du Colombier /* string is known to be NUL-terminated */
15459cc4ca5SDavid du Colombier Rune*
filelist(Text * t,Rune * r,int nr)15559cc4ca5SDavid du Colombier filelist(Text *t, Rune *r, int nr)
15659cc4ca5SDavid du Colombier {
15759cc4ca5SDavid du Colombier 	if(nr == 0)
15859cc4ca5SDavid du Colombier 		return nil;
15959cc4ca5SDavid du Colombier 	r = skipbl(r, nr, &nr);
16059cc4ca5SDavid du Colombier 	if(r[0] != '<')
16159cc4ca5SDavid du Colombier 		return runestrdup(r);
16259cc4ca5SDavid du Colombier 	/* use < command to collect text */
16359cc4ca5SDavid du Colombier 	clearcollection();
16459cc4ca5SDavid du Colombier 	runpipe(t, '<', r+1, nr-1, Collecting);
16559cc4ca5SDavid du Colombier 	return collection;
16659cc4ca5SDavid du Colombier }
16759cc4ca5SDavid du Colombier 
16859cc4ca5SDavid du Colombier int
a_cmd(Text * t,Cmd * cp)16959cc4ca5SDavid du Colombier a_cmd(Text *t, Cmd *cp)
17059cc4ca5SDavid du Colombier {
17159cc4ca5SDavid du Colombier 	return append(t->file, cp, addr.r.q1);
17259cc4ca5SDavid du Colombier }
17359cc4ca5SDavid du Colombier 
17459cc4ca5SDavid du Colombier int
b_cmd(Text *,Cmd * cp)17559cc4ca5SDavid du Colombier b_cmd(Text*, Cmd *cp)
17659cc4ca5SDavid du Colombier {
17759cc4ca5SDavid du Colombier 	File *f;
17859cc4ca5SDavid du Colombier 
17959cc4ca5SDavid du Colombier 	f = tofile(cp->text);
18059cc4ca5SDavid du Colombier 	if(nest == 0)
18159cc4ca5SDavid du Colombier 		pfilename(f);
18259cc4ca5SDavid du Colombier 	curtext = f->curtext;
18359cc4ca5SDavid du Colombier 	return TRUE;
18459cc4ca5SDavid du Colombier }
18559cc4ca5SDavid du Colombier 
18659cc4ca5SDavid du Colombier int
B_cmd(Text * t,Cmd * cp)18759cc4ca5SDavid du Colombier B_cmd(Text *t, Cmd *cp)
18859cc4ca5SDavid du Colombier {
18959cc4ca5SDavid du Colombier 	Rune *list, *r, *s;
19059cc4ca5SDavid du Colombier 	int nr;
19159cc4ca5SDavid du Colombier 
19259cc4ca5SDavid du Colombier 	list = filelist(t, cp->text->r, cp->text->n);
19359cc4ca5SDavid du Colombier 	if(list == nil)
19459cc4ca5SDavid du Colombier 		editerror(Enoname);
19559cc4ca5SDavid du Colombier 	r = list;
19659cc4ca5SDavid du Colombier 	nr = runestrlen(r);
19759cc4ca5SDavid du Colombier 	r = skipbl(r, nr, &nr);
19859cc4ca5SDavid du Colombier 	if(nr == 0)
19959cc4ca5SDavid du Colombier 		new(t, t, nil, 0, 0, r, 0);
20059cc4ca5SDavid du Colombier 	else while(nr > 0){
20159cc4ca5SDavid du Colombier 		s = findbl(r, nr, &nr);
20259cc4ca5SDavid du Colombier 		*s = '\0';
20359cc4ca5SDavid du Colombier 		new(t, t, nil, 0, 0, r, runestrlen(r));
20459cc4ca5SDavid du Colombier 		if(nr > 0)
20559cc4ca5SDavid du Colombier 			r = skipbl(s+1, nr-1, &nr);
20659cc4ca5SDavid du Colombier 	}
20759cc4ca5SDavid du Colombier 	clearcollection();
20859cc4ca5SDavid du Colombier 	return TRUE;
20959cc4ca5SDavid du Colombier }
21059cc4ca5SDavid du Colombier 
21159cc4ca5SDavid du Colombier int
c_cmd(Text * t,Cmd * cp)21259cc4ca5SDavid du Colombier c_cmd(Text *t, Cmd *cp)
21359cc4ca5SDavid du Colombier {
21459cc4ca5SDavid du Colombier 	elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n);
2156b6b9ac8SDavid du Colombier 	t->q0 = addr.r.q0;
2165ed2c76cSDavid du Colombier 	t->q1 = addr.r.q0;
21759cc4ca5SDavid du Colombier 	return TRUE;
21859cc4ca5SDavid du Colombier }
21959cc4ca5SDavid du Colombier 
22059cc4ca5SDavid du Colombier int
d_cmd(Text * t,Cmd *)22159cc4ca5SDavid du Colombier d_cmd(Text *t, Cmd*)
22259cc4ca5SDavid du Colombier {
22359cc4ca5SDavid du Colombier 	if(addr.r.q1 > addr.r.q0)
22459cc4ca5SDavid du Colombier 		elogdelete(t->file, addr.r.q0, addr.r.q1);
2256b6b9ac8SDavid du Colombier 	t->q0 = addr.r.q0;
2266b6b9ac8SDavid du Colombier 	t->q1 = addr.r.q0;
22759cc4ca5SDavid du Colombier 	return TRUE;
22859cc4ca5SDavid du Colombier }
22959cc4ca5SDavid du Colombier 
23059cc4ca5SDavid du Colombier void
D1(Text * t)23159cc4ca5SDavid du Colombier D1(Text *t)
23259cc4ca5SDavid du Colombier {
23359cc4ca5SDavid du Colombier 	if(t->w->body.file->ntext>1 || winclean(t->w, FALSE))
23459cc4ca5SDavid du Colombier 		colclose(t->col, t->w, TRUE);
23559cc4ca5SDavid du Colombier }
23659cc4ca5SDavid du Colombier 
23759cc4ca5SDavid du Colombier int
D_cmd(Text * t,Cmd * cp)23859cc4ca5SDavid du Colombier D_cmd(Text *t, Cmd *cp)
23959cc4ca5SDavid du Colombier {
24059cc4ca5SDavid du Colombier 	Rune *list, *r, *s, *n;
24159cc4ca5SDavid du Colombier 	int nr, nn;
24259cc4ca5SDavid du Colombier 	Window *w;
24359cc4ca5SDavid du Colombier 	Runestr dir, rs;
24459cc4ca5SDavid du Colombier 	char buf[128];
24559cc4ca5SDavid du Colombier 
24659cc4ca5SDavid du Colombier 	list = filelist(t, cp->text->r, cp->text->n);
24759cc4ca5SDavid du Colombier 	if(list == nil){
24859cc4ca5SDavid du Colombier 		D1(t);
24959cc4ca5SDavid du Colombier 		return TRUE;
25059cc4ca5SDavid du Colombier 	}
25159cc4ca5SDavid du Colombier 	dir = dirname(t, nil, 0);
25259cc4ca5SDavid du Colombier 	r = list;
25359cc4ca5SDavid du Colombier 	nr = runestrlen(r);
25459cc4ca5SDavid du Colombier 	r = skipbl(r, nr, &nr);
25559cc4ca5SDavid du Colombier 	do{
25659cc4ca5SDavid du Colombier 		s = findbl(r, nr, &nr);
25759cc4ca5SDavid du Colombier 		*s = '\0';
25859cc4ca5SDavid du Colombier 		/* first time through, could be empty string, meaning delete file empty name */
25959cc4ca5SDavid du Colombier 		nn = runestrlen(r);
26059cc4ca5SDavid du Colombier 		if(r[0]=='/' || nn==0 || dir.nr==0){
26159cc4ca5SDavid du Colombier 			rs.r = runestrdup(r);
26259cc4ca5SDavid du Colombier 			rs.nr = nn;
26359cc4ca5SDavid du Colombier 		}else{
26459cc4ca5SDavid du Colombier 			n = runemalloc(dir.nr+1+nn);
26559cc4ca5SDavid du Colombier 			runemove(n, dir.r, dir.nr);
26659cc4ca5SDavid du Colombier 			n[dir.nr] = '/';
26759cc4ca5SDavid du Colombier 			runemove(n+dir.nr+1, r, nn);
26859cc4ca5SDavid du Colombier 			rs = cleanrname((Runestr){n, dir.nr+1+nn});
26959cc4ca5SDavid du Colombier 		}
27059cc4ca5SDavid du Colombier 		w = lookfile(rs.r, rs.nr);
27159cc4ca5SDavid du Colombier 		if(w == nil){
27259cc4ca5SDavid du Colombier 			snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r);
27359cc4ca5SDavid du Colombier 			free(rs.r);
27459cc4ca5SDavid du Colombier 			editerror(buf);
27559cc4ca5SDavid du Colombier 		}
27659cc4ca5SDavid du Colombier 		free(rs.r);
27759cc4ca5SDavid du Colombier 		D1(&w->body);
27859cc4ca5SDavid du Colombier 		if(nr > 0)
27959cc4ca5SDavid du Colombier 			r = skipbl(s+1, nr-1, &nr);
28059cc4ca5SDavid du Colombier 	}while(nr > 0);
28159cc4ca5SDavid du Colombier 	clearcollection();
28259cc4ca5SDavid du Colombier 	free(dir.r);
28359cc4ca5SDavid du Colombier 	return TRUE;
28459cc4ca5SDavid du Colombier }
28559cc4ca5SDavid du Colombier 
28659cc4ca5SDavid du Colombier static int
readloader(void * v,uint q0,Rune * r,int nr)28759cc4ca5SDavid du Colombier readloader(void *v, uint q0, Rune *r, int nr)
28859cc4ca5SDavid du Colombier {
28959cc4ca5SDavid du Colombier 	if(nr > 0)
29059cc4ca5SDavid du Colombier 		eloginsert(v, q0, r, nr);
29159cc4ca5SDavid du Colombier 	return 0;
29259cc4ca5SDavid du Colombier }
29359cc4ca5SDavid du Colombier 
29459cc4ca5SDavid du Colombier int
e_cmd(Text * t,Cmd * cp)29559cc4ca5SDavid du Colombier e_cmd(Text *t, Cmd *cp)
29659cc4ca5SDavid du Colombier {
29759cc4ca5SDavid du Colombier 	Rune *name;
29859cc4ca5SDavid du Colombier 	File *f;
2999a747e4fSDavid du Colombier 	int i, isdir, q0, q1, fd, nulls, samename, allreplaced;
30059cc4ca5SDavid du Colombier 	char *s, tmp[128];
3019a747e4fSDavid du Colombier 	Dir *d;
30259cc4ca5SDavid du Colombier 
30359cc4ca5SDavid du Colombier 	f = t->file;
30459cc4ca5SDavid du Colombier 	q0 = addr.r.q0;
30559cc4ca5SDavid du Colombier 	q1 = addr.r.q1;
30659cc4ca5SDavid du Colombier 	if(cp->cmdc == 'e'){
30759cc4ca5SDavid du Colombier 		if(winclean(t->w, TRUE)==FALSE)
30859cc4ca5SDavid du Colombier 			editerror("");	/* winclean generated message already */
30959cc4ca5SDavid du Colombier 		q0 = 0;
31059cc4ca5SDavid du Colombier 		q1 = f->nc;
31159cc4ca5SDavid du Colombier 	}
31259cc4ca5SDavid du Colombier 	allreplaced = (q0==0 && q1==f->nc);
31359cc4ca5SDavid du Colombier 	name = cmdname(f, cp->text, cp->cmdc=='e');
31459cc4ca5SDavid du Colombier 	if(name == nil)
31559cc4ca5SDavid du Colombier 		editerror(Enoname);
31659cc4ca5SDavid du Colombier 	i = runestrlen(name);
31759cc4ca5SDavid du Colombier 	samename = runeeq(name, i, t->file->name, t->file->nname);
31859cc4ca5SDavid du Colombier 	s = runetobyte(name, i);
31959cc4ca5SDavid du Colombier 	free(name);
32059cc4ca5SDavid du Colombier 	fd = open(s, OREAD);
32159cc4ca5SDavid du Colombier 	if(fd < 0){
32259cc4ca5SDavid du Colombier 		snprint(tmp, sizeof tmp, "can't open %s: %r", s);
32359cc4ca5SDavid du Colombier 		free(s);
32459cc4ca5SDavid du Colombier 		editerror(tmp);
32559cc4ca5SDavid du Colombier 	}
3269a747e4fSDavid du Colombier 	d = dirfstat(fd);
3279a747e4fSDavid du Colombier 	isdir = (d!=nil && (d->qid.type&QTDIR));
3289a747e4fSDavid du Colombier 	free(d);
3299a747e4fSDavid du Colombier 	if(isdir){
33059cc4ca5SDavid du Colombier 		close(fd);
33159cc4ca5SDavid du Colombier 		snprint(tmp, sizeof tmp, "%s is a directory", s);
33259cc4ca5SDavid du Colombier 		free(s);
33359cc4ca5SDavid du Colombier 		editerror(tmp);
33459cc4ca5SDavid du Colombier 	}
33559cc4ca5SDavid du Colombier 	elogdelete(f, q0, q1);
33659cc4ca5SDavid du Colombier 	nulls = 0;
33759cc4ca5SDavid du Colombier 	loadfile(fd, q1, &nulls, readloader, f);
33859cc4ca5SDavid du Colombier 	free(s);
33959cc4ca5SDavid du Colombier 	close(fd);
34059cc4ca5SDavid du Colombier 	if(nulls)
34159cc4ca5SDavid du Colombier 		warning(nil, "%s: NUL bytes elided\n", s);
34259cc4ca5SDavid du Colombier 	else if(allreplaced && samename)
34359cc4ca5SDavid du Colombier 		f->editclean = TRUE;
34459cc4ca5SDavid du Colombier 	return TRUE;
34559cc4ca5SDavid du Colombier }
34659cc4ca5SDavid du Colombier 
34759cc4ca5SDavid du Colombier int
f_cmd(Text * t,Cmd * cp)34859cc4ca5SDavid du Colombier f_cmd(Text *t, Cmd *cp)
34959cc4ca5SDavid du Colombier {
35059cc4ca5SDavid du Colombier 	Rune *name;
351d9dc5dd1SDavid du Colombier 	String *str;
352d9dc5dd1SDavid du Colombier 	String empty;
35359cc4ca5SDavid du Colombier 
354d9dc5dd1SDavid du Colombier 	if(cp->text == nil){
355d9dc5dd1SDavid du Colombier 		empty.n = 0;
356d9dc5dd1SDavid du Colombier 		empty.r = L"";
357d9dc5dd1SDavid du Colombier 		str = &empty;
358d9dc5dd1SDavid du Colombier 	}else
359d9dc5dd1SDavid du Colombier 		str = cp->text;
360d9dc5dd1SDavid du Colombier 	name = cmdname(t->file, str, TRUE);
36159cc4ca5SDavid du Colombier 	free(name);
36259cc4ca5SDavid du Colombier 	pfilename(t->file);
36359cc4ca5SDavid du Colombier 	return TRUE;
36459cc4ca5SDavid du Colombier }
36559cc4ca5SDavid du Colombier 
36659cc4ca5SDavid du Colombier int
g_cmd(Text * t,Cmd * cp)36759cc4ca5SDavid du Colombier g_cmd(Text *t, Cmd *cp)
36859cc4ca5SDavid du Colombier {
36959cc4ca5SDavid du Colombier 	if(t->file != addr.f){
37059cc4ca5SDavid du Colombier 		warning(nil, "internal error: g_cmd f!=addr.f\n");
37159cc4ca5SDavid du Colombier 		return FALSE;
37259cc4ca5SDavid du Colombier 	}
37380ee5cbfSDavid du Colombier 	if(rxcompile(cp->re->r) == FALSE)
37480ee5cbfSDavid du Colombier 		editerror("bad regexp in g command");
37559cc4ca5SDavid du Colombier 	if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){
37659cc4ca5SDavid du Colombier 		t->q0 = addr.r.q0;
37759cc4ca5SDavid du Colombier 		t->q1 = addr.r.q1;
37859cc4ca5SDavid du Colombier 		return cmdexec(t, cp->cmd);
37959cc4ca5SDavid du Colombier 	}
38059cc4ca5SDavid du Colombier 	return TRUE;
38159cc4ca5SDavid du Colombier }
38259cc4ca5SDavid du Colombier 
38359cc4ca5SDavid du Colombier int
i_cmd(Text * t,Cmd * cp)38459cc4ca5SDavid du Colombier i_cmd(Text *t, Cmd *cp)
38559cc4ca5SDavid du Colombier {
38659cc4ca5SDavid du Colombier 	return append(t->file, cp, addr.r.q0);
38759cc4ca5SDavid du Colombier }
38859cc4ca5SDavid du Colombier 
38959cc4ca5SDavid du Colombier void
copy(File * f,Address addr2)39059cc4ca5SDavid du Colombier copy(File *f, Address addr2)
39159cc4ca5SDavid du Colombier {
39259cc4ca5SDavid du Colombier 	long p;
39359cc4ca5SDavid du Colombier 	int ni;
39459cc4ca5SDavid du Colombier 	Rune *buf;
39559cc4ca5SDavid du Colombier 
39659cc4ca5SDavid du Colombier 	buf = fbufalloc();
39759cc4ca5SDavid du Colombier 	for(p=addr.r.q0; p<addr.r.q1; p+=ni){
39859cc4ca5SDavid du Colombier 		ni = addr.r.q1-p;
39959cc4ca5SDavid du Colombier 		if(ni > RBUFSIZE)
40059cc4ca5SDavid du Colombier 			ni = RBUFSIZE;
40159cc4ca5SDavid du Colombier 		bufread(f, p, buf, ni);
40259cc4ca5SDavid du Colombier 		eloginsert(addr2.f, addr2.r.q1, buf, ni);
40359cc4ca5SDavid du Colombier 	}
40459cc4ca5SDavid du Colombier 	fbuffree(buf);
40559cc4ca5SDavid du Colombier }
40659cc4ca5SDavid du Colombier 
40759cc4ca5SDavid du Colombier void
move(File * f,Address addr2)40859cc4ca5SDavid du Colombier move(File *f, Address addr2)
40959cc4ca5SDavid du Colombier {
41059cc4ca5SDavid du Colombier 	if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){
41159cc4ca5SDavid du Colombier 		elogdelete(f, addr.r.q0, addr.r.q1);
41259cc4ca5SDavid du Colombier 		copy(f, addr2);
41359cc4ca5SDavid du Colombier 	}else if(addr.r.q0 >= addr2.r.q1){
41459cc4ca5SDavid du Colombier 		copy(f, addr2);
41559cc4ca5SDavid du Colombier 		elogdelete(f, addr.r.q0, addr.r.q1);
416*de7131e1SDavid du Colombier 	}else if(addr.r.q0==addr2.r.q0 && addr.r.q1==addr2.r.q1){
417*de7131e1SDavid du Colombier 		;	/* move to self; no-op */
41859cc4ca5SDavid du Colombier 	}else
419*de7131e1SDavid du Colombier 		editerror("move overlaps itself");
42059cc4ca5SDavid du Colombier }
42159cc4ca5SDavid du Colombier 
42259cc4ca5SDavid du Colombier int
m_cmd(Text * t,Cmd * cp)42359cc4ca5SDavid du Colombier m_cmd(Text *t, Cmd *cp)
42459cc4ca5SDavid du Colombier {
42559cc4ca5SDavid du Colombier 	Address dot, addr2;
42659cc4ca5SDavid du Colombier 
42759cc4ca5SDavid du Colombier 	mkaddr(&dot, t->file);
42859cc4ca5SDavid du Colombier 	addr2 = cmdaddress(cp->mtaddr, dot, 0);
42959cc4ca5SDavid du Colombier 	if(cp->cmdc == 'm')
43059cc4ca5SDavid du Colombier 		move(t->file, addr2);
43159cc4ca5SDavid du Colombier 	else
43259cc4ca5SDavid du Colombier 		copy(t->file, addr2);
43359cc4ca5SDavid du Colombier 	return TRUE;
43459cc4ca5SDavid du Colombier }
43559cc4ca5SDavid du Colombier 
43659cc4ca5SDavid du Colombier int
p_cmd(Text * t,Cmd *)43759cc4ca5SDavid du Colombier p_cmd(Text *t, Cmd*)
43859cc4ca5SDavid du Colombier {
43959cc4ca5SDavid du Colombier 	return pdisplay(t->file);
44059cc4ca5SDavid du Colombier }
44159cc4ca5SDavid du Colombier 
44259cc4ca5SDavid du Colombier int
s_cmd(Text * t,Cmd * cp)44359cc4ca5SDavid du Colombier s_cmd(Text *t, Cmd *cp)
44459cc4ca5SDavid du Colombier {
44559cc4ca5SDavid du Colombier 	int i, j, k, c, m, n, nrp, didsub;
44659cc4ca5SDavid du Colombier 	long p1, op, delta;
44759cc4ca5SDavid du Colombier 	String *buf;
44859cc4ca5SDavid du Colombier 	Rangeset *rp;
44959cc4ca5SDavid du Colombier 	char *err;
45059cc4ca5SDavid du Colombier 	Rune *rbuf;
45159cc4ca5SDavid du Colombier 
45259cc4ca5SDavid du Colombier 	n = cp->num;
45359cc4ca5SDavid du Colombier 	op= -1;
45480ee5cbfSDavid du Colombier 	if(rxcompile(cp->re->r) == FALSE)
45580ee5cbfSDavid du Colombier 		editerror("bad regexp in s command");
45659cc4ca5SDavid du Colombier 	nrp = 0;
45759cc4ca5SDavid du Colombier 	rp = nil;
45859cc4ca5SDavid du Colombier 	delta = 0;
45959cc4ca5SDavid du Colombier 	didsub = FALSE;
46059cc4ca5SDavid du Colombier 	for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){
46159cc4ca5SDavid du Colombier 		if(sel.r[0].q0 == sel.r[0].q1){	/* empty match? */
46259cc4ca5SDavid du Colombier 			if(sel.r[0].q0 == op){
46359cc4ca5SDavid du Colombier 				p1++;
46459cc4ca5SDavid du Colombier 				continue;
46559cc4ca5SDavid du Colombier 			}
46659cc4ca5SDavid du Colombier 			p1 = sel.r[0].q1+1;
46759cc4ca5SDavid du Colombier 		}else
46859cc4ca5SDavid du Colombier 			p1 = sel.r[0].q1;
46959cc4ca5SDavid du Colombier 		op = sel.r[0].q1;
47059cc4ca5SDavid du Colombier 		if(--n>0)
47159cc4ca5SDavid du Colombier 			continue;
47259cc4ca5SDavid du Colombier 		nrp++;
47359cc4ca5SDavid du Colombier 		rp = erealloc(rp, nrp*sizeof(Rangeset));
47459cc4ca5SDavid du Colombier 		rp[nrp-1] = sel;
47559cc4ca5SDavid du Colombier 	}
47659cc4ca5SDavid du Colombier 	rbuf = fbufalloc();
47759cc4ca5SDavid du Colombier 	buf = allocstring(0);
47859cc4ca5SDavid du Colombier 	for(m=0; m<nrp; m++){
47959cc4ca5SDavid du Colombier 		buf->n = 0;
48059cc4ca5SDavid du Colombier 		buf->r[0] = L'\0';
48159cc4ca5SDavid du Colombier 		sel = rp[m];
48259cc4ca5SDavid du Colombier 		for(i = 0; i<cp->text->n; i++)
48359cc4ca5SDavid du Colombier 			if((c = cp->text->r[i])=='\\' && i<cp->text->n-1){
48459cc4ca5SDavid du Colombier 				c = cp->text->r[++i];
48559cc4ca5SDavid du Colombier 				if('1'<=c && c<='9') {
48659cc4ca5SDavid du Colombier 					j = c-'0';
48759cc4ca5SDavid du Colombier 					if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){
48859cc4ca5SDavid du Colombier 						err = "replacement string too long";
48959cc4ca5SDavid du Colombier 						goto Err;
49059cc4ca5SDavid du Colombier 					}
49159cc4ca5SDavid du Colombier 					bufread(t->file, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0);
49259cc4ca5SDavid du Colombier 					for(k=0; k<sel.r[j].q1-sel.r[j].q0; k++)
49359cc4ca5SDavid du Colombier 						Straddc(buf, rbuf[k]);
49459cc4ca5SDavid du Colombier 				}else
49559cc4ca5SDavid du Colombier 				 	Straddc(buf, c);
49659cc4ca5SDavid du Colombier 			}else if(c!='&')
49759cc4ca5SDavid du Colombier 				Straddc(buf, c);
49859cc4ca5SDavid du Colombier 			else{
49959cc4ca5SDavid du Colombier 				if(sel.r[0].q1-sel.r[0].q0>RBUFSIZE){
50059cc4ca5SDavid du Colombier 					err = "right hand side too long in substitution";
50159cc4ca5SDavid du Colombier 					goto Err;
50259cc4ca5SDavid du Colombier 				}
50359cc4ca5SDavid du Colombier 				bufread(t->file, sel.r[0].q0, rbuf, sel.r[0].q1-sel.r[0].q0);
50459cc4ca5SDavid du Colombier 				for(k=0; k<sel.r[0].q1-sel.r[0].q0; k++)
50559cc4ca5SDavid du Colombier 					Straddc(buf, rbuf[k]);
50659cc4ca5SDavid du Colombier 			}
50759cc4ca5SDavid du Colombier 		elogreplace(t->file, sel.r[0].q0, sel.r[0].q1,  buf->r, buf->n);
50859cc4ca5SDavid du Colombier 		delta -= sel.r[0].q1-sel.r[0].q0;
50959cc4ca5SDavid du Colombier 		delta += buf->n;
51059cc4ca5SDavid du Colombier 		didsub = 1;
51159cc4ca5SDavid du Colombier 		if(!cp->flag)
51259cc4ca5SDavid du Colombier 			break;
51359cc4ca5SDavid du Colombier 	}
51459cc4ca5SDavid du Colombier 	free(rp);
51559cc4ca5SDavid du Colombier 	freestring(buf);
51659cc4ca5SDavid du Colombier 	fbuffree(rbuf);
51759cc4ca5SDavid du Colombier 	if(!didsub && nest==0)
51859cc4ca5SDavid du Colombier 		editerror("no substitution");
51959cc4ca5SDavid du Colombier 	t->q0 = addr.r.q0;
5205ed2c76cSDavid du Colombier 	t->q1 = addr.r.q1;
52159cc4ca5SDavid du Colombier 	return TRUE;
52259cc4ca5SDavid du Colombier 
52359cc4ca5SDavid du Colombier Err:
52459cc4ca5SDavid du Colombier 	free(rp);
52559cc4ca5SDavid du Colombier 	freestring(buf);
52659cc4ca5SDavid du Colombier 	fbuffree(rbuf);
52759cc4ca5SDavid du Colombier 	editerror(err);
52859cc4ca5SDavid du Colombier 	return FALSE;
52959cc4ca5SDavid du Colombier }
53059cc4ca5SDavid du Colombier 
53159cc4ca5SDavid du Colombier int
u_cmd(Text * t,Cmd * cp)53259cc4ca5SDavid du Colombier u_cmd(Text *t, Cmd *cp)
53359cc4ca5SDavid du Colombier {
53459cc4ca5SDavid du Colombier 	int n, oseq, flag;
53559cc4ca5SDavid du Colombier 
53659cc4ca5SDavid du Colombier 	n = cp->num;
53759cc4ca5SDavid du Colombier 	flag = TRUE;
53859cc4ca5SDavid du Colombier 	if(n < 0){
53959cc4ca5SDavid du Colombier 		n = -n;
54059cc4ca5SDavid du Colombier 		flag = FALSE;
54159cc4ca5SDavid du Colombier 	}
54259cc4ca5SDavid du Colombier 	oseq = -1;
54359cc4ca5SDavid du Colombier 	while(n-->0 && t->file->seq!=0 && t->file->seq!=oseq){
54459cc4ca5SDavid du Colombier 		oseq = t->file->seq;
54559cc4ca5SDavid du Colombier 		undo(t, nil, nil, flag, 0, nil, 0);
54659cc4ca5SDavid du Colombier 	}
54759cc4ca5SDavid du Colombier 	return TRUE;
54859cc4ca5SDavid du Colombier }
54959cc4ca5SDavid du Colombier 
55059cc4ca5SDavid du Colombier int
w_cmd(Text * t,Cmd * cp)55159cc4ca5SDavid du Colombier w_cmd(Text *t, Cmd *cp)
55259cc4ca5SDavid du Colombier {
55359cc4ca5SDavid du Colombier 	Rune *r;
55459cc4ca5SDavid du Colombier 	File *f;
55559cc4ca5SDavid du Colombier 
55659cc4ca5SDavid du Colombier 	f = t->file;
55759cc4ca5SDavid du Colombier 	if(f->seq == seq)
55859cc4ca5SDavid du Colombier 		editerror("can't write file with pending modifications");
55959cc4ca5SDavid du Colombier 	r = cmdname(f, cp->text, FALSE);
56059cc4ca5SDavid du Colombier 	if(r == nil)
56180ee5cbfSDavid du Colombier 		editerror("no name specified for 'w' command");
56259cc4ca5SDavid du Colombier 	putfile(f, addr.r.q0, addr.r.q1, r, runestrlen(r));
56359cc4ca5SDavid du Colombier 	/* r is freed by putfile */
56459cc4ca5SDavid du Colombier 	return TRUE;
56559cc4ca5SDavid du Colombier }
56659cc4ca5SDavid du Colombier 
56759cc4ca5SDavid du Colombier int
x_cmd(Text * t,Cmd * cp)56859cc4ca5SDavid du Colombier x_cmd(Text *t, Cmd *cp)
56959cc4ca5SDavid du Colombier {
57059cc4ca5SDavid du Colombier 	if(cp->re)
57159cc4ca5SDavid du Colombier 		looper(t->file, cp, cp->cmdc=='x');
57259cc4ca5SDavid du Colombier 	else
57359cc4ca5SDavid du Colombier 		linelooper(t->file, cp);
57459cc4ca5SDavid du Colombier 	return TRUE;
57559cc4ca5SDavid du Colombier }
57659cc4ca5SDavid du Colombier 
57759cc4ca5SDavid du Colombier int
X_cmd(Text *,Cmd * cp)57859cc4ca5SDavid du Colombier X_cmd(Text*, Cmd *cp)
57959cc4ca5SDavid du Colombier {
58059cc4ca5SDavid du Colombier 	filelooper(cp, cp->cmdc=='X');
58159cc4ca5SDavid du Colombier 	return TRUE;
58259cc4ca5SDavid du Colombier }
58359cc4ca5SDavid du Colombier 
58459cc4ca5SDavid du Colombier void
runpipe(Text * t,int cmd,Rune * cr,int ncr,int state)58559cc4ca5SDavid du Colombier runpipe(Text *t, int cmd, Rune *cr, int ncr, int state)
58659cc4ca5SDavid du Colombier {
58759cc4ca5SDavid du Colombier 	Rune *r, *s;
58859cc4ca5SDavid du Colombier 	int n;
58959cc4ca5SDavid du Colombier 	Runestr dir;
59059cc4ca5SDavid du Colombier 	Window *w;
59159cc4ca5SDavid du Colombier 
59259cc4ca5SDavid du Colombier 	r = skipbl(cr, ncr, &n);
59359cc4ca5SDavid du Colombier 	if(n == 0)
5944fec87e5SDavid du Colombier 		editerror("no command specified for %c", cmd);
59559cc4ca5SDavid du Colombier 	w = nil;
59659cc4ca5SDavid du Colombier 	if(state == Inserting){
59759cc4ca5SDavid du Colombier 		w = t->w;
59859cc4ca5SDavid du Colombier 		t->q0 = addr.r.q0;
59959cc4ca5SDavid du Colombier 		t->q1 = addr.r.q1;
60059cc4ca5SDavid du Colombier 		if(cmd == '<' || cmd=='|')
60159cc4ca5SDavid du Colombier 			elogdelete(t->file, t->q0, t->q1);
60259cc4ca5SDavid du Colombier 	}
60359cc4ca5SDavid du Colombier 	s = runemalloc(n+2);
60459cc4ca5SDavid du Colombier 	s[0] = cmd;
60559cc4ca5SDavid du Colombier 	runemove(s+1, r, n);
60659cc4ca5SDavid du Colombier 	n++;
60759cc4ca5SDavid du Colombier 	dir.r = nil;
60859cc4ca5SDavid du Colombier 	dir.nr = 0;
60959cc4ca5SDavid du Colombier 	if(t != nil)
61059cc4ca5SDavid du Colombier 		dir = dirname(t, nil, 0);
61159cc4ca5SDavid du Colombier 	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
61259cc4ca5SDavid du Colombier 		free(dir.r);
61359cc4ca5SDavid du Colombier 		dir.r = nil;
61459cc4ca5SDavid du Colombier 		dir.nr = 0;
61559cc4ca5SDavid du Colombier 	}
61659cc4ca5SDavid du Colombier 	editing = state;
61759cc4ca5SDavid du Colombier 	if(t!=nil && t->w!=nil)
61859cc4ca5SDavid du Colombier 		incref(t->w);	/* run will decref */
61959cc4ca5SDavid du Colombier 	run(w, runetobyte(s, n), dir.r, dir.nr, TRUE, nil, nil, TRUE);
62059cc4ca5SDavid du Colombier 	free(s);
62159cc4ca5SDavid du Colombier 	if(t!=nil && t->w!=nil)
62259cc4ca5SDavid du Colombier 		winunlock(t->w);
62359cc4ca5SDavid du Colombier 	qunlock(&row);
62459cc4ca5SDavid du Colombier 	recvul(cedit);
62559cc4ca5SDavid du Colombier 	qlock(&row);
62659cc4ca5SDavid du Colombier 	editing = Inactive;
62759cc4ca5SDavid du Colombier 	if(t!=nil && t->w!=nil)
62859cc4ca5SDavid du Colombier 		winlock(t->w, 'M');
62959cc4ca5SDavid du Colombier }
63059cc4ca5SDavid du Colombier 
63159cc4ca5SDavid du Colombier int
pipe_cmd(Text * t,Cmd * cp)63259cc4ca5SDavid du Colombier pipe_cmd(Text *t, Cmd *cp)
63359cc4ca5SDavid du Colombier {
63459cc4ca5SDavid du Colombier 	runpipe(t, cp->cmdc, cp->text->r, cp->text->n, Inserting);
63559cc4ca5SDavid du Colombier 	return TRUE;
63659cc4ca5SDavid du Colombier }
63759cc4ca5SDavid du Colombier 
63859cc4ca5SDavid du Colombier long
nlcount(Text * t,long q0,long q1)63959cc4ca5SDavid du Colombier nlcount(Text *t, long q0, long q1)
64059cc4ca5SDavid du Colombier {
64159cc4ca5SDavid du Colombier 	long nl;
64259cc4ca5SDavid du Colombier 	Rune *buf;
64359cc4ca5SDavid du Colombier 	int i, nbuf;
64459cc4ca5SDavid du Colombier 
64559cc4ca5SDavid du Colombier 	buf = fbufalloc();
64659cc4ca5SDavid du Colombier 	nbuf = 0;
64759cc4ca5SDavid du Colombier 	i = nl = 0;
64859cc4ca5SDavid du Colombier 	while(q0 < q1){
64959cc4ca5SDavid du Colombier 		if(i == nbuf){
65059cc4ca5SDavid du Colombier 			nbuf = q1-q0;
65159cc4ca5SDavid du Colombier 			if(nbuf > RBUFSIZE)
65259cc4ca5SDavid du Colombier 				nbuf = RBUFSIZE;
65359cc4ca5SDavid du Colombier 			bufread(t->file, q0, buf, nbuf);
65459cc4ca5SDavid du Colombier 			i = 0;
65559cc4ca5SDavid du Colombier 		}
65659cc4ca5SDavid du Colombier 		if(buf[i++] == '\n')
65759cc4ca5SDavid du Colombier 			nl++;
65859cc4ca5SDavid du Colombier 		q0++;
65959cc4ca5SDavid du Colombier 	}
66059cc4ca5SDavid du Colombier 	fbuffree(buf);
66159cc4ca5SDavid du Colombier 	return nl;
66259cc4ca5SDavid du Colombier }
66359cc4ca5SDavid du Colombier 
66459cc4ca5SDavid du Colombier void
printposn(Text * t,int charsonly)66559cc4ca5SDavid du Colombier printposn(Text *t, int charsonly)
66659cc4ca5SDavid du Colombier {
66759cc4ca5SDavid du Colombier 	long l1, l2;
66859cc4ca5SDavid du Colombier 
66980ee5cbfSDavid du Colombier 	if (t != nil && t->file != nil && t->file->name != nil)
67080ee5cbfSDavid du Colombier 		warning(nil, "%.*S:", t->file->nname, t->file->name);
67159cc4ca5SDavid du Colombier 	if(!charsonly){
67259cc4ca5SDavid du Colombier 		l1 = 1+nlcount(t, 0, addr.r.q0);
67359cc4ca5SDavid du Colombier 		l2 = l1+nlcount(t, addr.r.q0, addr.r.q1);
67459cc4ca5SDavid du Colombier 		/* check if addr ends with '\n' */
67559cc4ca5SDavid du Colombier 		if(addr.r.q1>0 && addr.r.q1>addr.r.q0 && textreadc(t, addr.r.q1-1)=='\n')
67659cc4ca5SDavid du Colombier 			--l2;
67759cc4ca5SDavid du Colombier 		warning(nil, "%lud", l1);
67859cc4ca5SDavid du Colombier 		if(l2 != l1)
67959cc4ca5SDavid du Colombier 			warning(nil, ",%lud", l2);
68080ee5cbfSDavid du Colombier 		warning(nil, "\n");
68180ee5cbfSDavid du Colombier 		return;
68259cc4ca5SDavid du Colombier 	}
68359cc4ca5SDavid du Colombier 	warning(nil, "#%d", addr.r.q0);
68459cc4ca5SDavid du Colombier 	if(addr.r.q1 != addr.r.q0)
68559cc4ca5SDavid du Colombier 		warning(nil, ",#%d", addr.r.q1);
68659cc4ca5SDavid du Colombier 	warning(nil, "\n");
68759cc4ca5SDavid du Colombier }
68859cc4ca5SDavid du Colombier 
68959cc4ca5SDavid du Colombier int
eq_cmd(Text * t,Cmd * cp)69059cc4ca5SDavid du Colombier eq_cmd(Text *t, Cmd *cp)
69159cc4ca5SDavid du Colombier {
69259cc4ca5SDavid du Colombier 	int charsonly;
69359cc4ca5SDavid du Colombier 
69459cc4ca5SDavid du Colombier 	switch(cp->text->n){
69559cc4ca5SDavid du Colombier 	case 0:
69659cc4ca5SDavid du Colombier 		charsonly = FALSE;
69759cc4ca5SDavid du Colombier 		break;
69859cc4ca5SDavid du Colombier 	case 1:
69959cc4ca5SDavid du Colombier 		if(cp->text->r[0] == '#'){
70059cc4ca5SDavid du Colombier 			charsonly = TRUE;
70159cc4ca5SDavid du Colombier 			break;
70259cc4ca5SDavid du Colombier 		}
70359cc4ca5SDavid du Colombier 	default:
70459cc4ca5SDavid du Colombier 		SET(charsonly);
70559cc4ca5SDavid du Colombier 		editerror("newline expected");
70659cc4ca5SDavid du Colombier 	}
70759cc4ca5SDavid du Colombier 	printposn(t, charsonly);
70859cc4ca5SDavid du Colombier 	return TRUE;
70959cc4ca5SDavid du Colombier }
71059cc4ca5SDavid du Colombier 
71159cc4ca5SDavid du Colombier int
nl_cmd(Text * t,Cmd * cp)71259cc4ca5SDavid du Colombier nl_cmd(Text *t, Cmd *cp)
71359cc4ca5SDavid du Colombier {
71459cc4ca5SDavid du Colombier 	Address a;
71559cc4ca5SDavid du Colombier 	File *f;
71659cc4ca5SDavid du Colombier 
71759cc4ca5SDavid du Colombier 	f = t->file;
71859cc4ca5SDavid du Colombier 	if(cp->addr == 0){
71959cc4ca5SDavid du Colombier 		/* First put it on newline boundaries */
72059cc4ca5SDavid du Colombier 		mkaddr(&a, f);
72159cc4ca5SDavid du Colombier 		addr = lineaddr(0, a, -1);
72259cc4ca5SDavid du Colombier 		a = lineaddr(0, a, 1);
72359cc4ca5SDavid du Colombier 		addr.r.q1 = a.r.q1;
72459cc4ca5SDavid du Colombier 		if(addr.r.q0==t->q0 && addr.r.q1==t->q1){
72559cc4ca5SDavid du Colombier 			mkaddr(&a, f);
72659cc4ca5SDavid du Colombier 			addr = lineaddr(1, a, 1);
72759cc4ca5SDavid du Colombier 		}
72859cc4ca5SDavid du Colombier 	}
7299a747e4fSDavid du Colombier 	textshow(t, addr.r.q0, addr.r.q1, 1);
73059cc4ca5SDavid du Colombier 	return TRUE;
73159cc4ca5SDavid du Colombier }
73259cc4ca5SDavid du Colombier 
73359cc4ca5SDavid du Colombier int
append(File * f,Cmd * cp,long p)73459cc4ca5SDavid du Colombier append(File *f, Cmd *cp, long p)
73559cc4ca5SDavid du Colombier {
73659cc4ca5SDavid du Colombier 	if(cp->text->n > 0)
73759cc4ca5SDavid du Colombier 		eloginsert(f, p, cp->text->r, cp->text->n);
7386b6b9ac8SDavid du Colombier 	f->curtext->q0 = p;
7395ed2c76cSDavid du Colombier 	f->curtext->q1 = p;
74059cc4ca5SDavid du Colombier 	return TRUE;
74159cc4ca5SDavid du Colombier }
74259cc4ca5SDavid du Colombier 
74359cc4ca5SDavid du Colombier int
pdisplay(File * f)74459cc4ca5SDavid du Colombier pdisplay(File *f)
74559cc4ca5SDavid du Colombier {
74659cc4ca5SDavid du Colombier 	long p1, p2;
74759cc4ca5SDavid du Colombier 	int np;
74859cc4ca5SDavid du Colombier 	Rune *buf;
74959cc4ca5SDavid du Colombier 
75059cc4ca5SDavid du Colombier 	p1 = addr.r.q0;
75159cc4ca5SDavid du Colombier 	p2 = addr.r.q1;
75259cc4ca5SDavid du Colombier 	if(p2 > f->nc)
75359cc4ca5SDavid du Colombier 		p2 = f->nc;
75459cc4ca5SDavid du Colombier 	buf = fbufalloc();
75559cc4ca5SDavid du Colombier 	while(p1 < p2){
75659cc4ca5SDavid du Colombier 		np = p2-p1;
75759cc4ca5SDavid du Colombier 		if(np>RBUFSIZE-1)
75859cc4ca5SDavid du Colombier 			np = RBUFSIZE-1;
75959cc4ca5SDavid du Colombier 		bufread(f, p1, buf, np);
76059cc4ca5SDavid du Colombier 		buf[np] = L'\0';
76159cc4ca5SDavid du Colombier 		warning(nil, "%S", buf);
76259cc4ca5SDavid du Colombier 		p1 += np;
76359cc4ca5SDavid du Colombier 	}
76459cc4ca5SDavid du Colombier 	fbuffree(buf);
76559cc4ca5SDavid du Colombier 	f->curtext->q0 = addr.r.q0;
76659cc4ca5SDavid du Colombier 	f->curtext->q1 = addr.r.q1;
76759cc4ca5SDavid du Colombier 	return TRUE;
76859cc4ca5SDavid du Colombier }
76959cc4ca5SDavid du Colombier 
77059cc4ca5SDavid du Colombier void
pfilename(File * f)77159cc4ca5SDavid du Colombier pfilename(File *f)
77259cc4ca5SDavid du Colombier {
77380ee5cbfSDavid du Colombier 	int dirty;
77480ee5cbfSDavid du Colombier 	Window *w;
77580ee5cbfSDavid du Colombier 
77680ee5cbfSDavid du Colombier 	w = f->curtext->w;
77780ee5cbfSDavid du Colombier 	/* same check for dirty as in settag, but we know ncache==0 */
77880ee5cbfSDavid du Colombier 	dirty = !w->isdir && !w->isscratch && f->mod;
77980ee5cbfSDavid du Colombier 	warning(nil, "%c%c%c %.*S\n", " '"[dirty],
78059cc4ca5SDavid du Colombier 		'+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
78159cc4ca5SDavid du Colombier }
78259cc4ca5SDavid du Colombier 
78359cc4ca5SDavid du Colombier void
loopcmd(File * f,Cmd * cp,Range * rp,long nrp)78459cc4ca5SDavid du Colombier loopcmd(File *f, Cmd *cp, Range *rp, long nrp)
78559cc4ca5SDavid du Colombier {
78659cc4ca5SDavid du Colombier 	long i;
78759cc4ca5SDavid du Colombier 
78859cc4ca5SDavid du Colombier 	for(i=0; i<nrp; i++){
78959cc4ca5SDavid du Colombier 		f->curtext->q0 = rp[i].q0;
79059cc4ca5SDavid du Colombier 		f->curtext->q1 = rp[i].q1;
79159cc4ca5SDavid du Colombier 		cmdexec(f->curtext, cp);
79259cc4ca5SDavid du Colombier 	}
79359cc4ca5SDavid du Colombier }
79459cc4ca5SDavid du Colombier 
79559cc4ca5SDavid du Colombier void
looper(File * f,Cmd * cp,int xy)79659cc4ca5SDavid du Colombier looper(File *f, Cmd *cp, int xy)
79759cc4ca5SDavid du Colombier {
79859cc4ca5SDavid du Colombier 	long p, op, nrp;
79959cc4ca5SDavid du Colombier 	Range r, tr;
80059cc4ca5SDavid du Colombier 	Range *rp;
80159cc4ca5SDavid du Colombier 
80259cc4ca5SDavid du Colombier 	r = addr.r;
80359cc4ca5SDavid du Colombier 	op= xy? -1 : r.q0;
80459cc4ca5SDavid du Colombier 	nest++;
80580ee5cbfSDavid du Colombier 	if(rxcompile(cp->re->r) == FALSE)
80680ee5cbfSDavid du Colombier 		editerror("bad regexp in %c command", cp->cmdc);
80759cc4ca5SDavid du Colombier 	nrp = 0;
80859cc4ca5SDavid du Colombier 	rp = nil;
80959cc4ca5SDavid du Colombier 	for(p = r.q0; p<=r.q1; ){
81059cc4ca5SDavid du Colombier 		if(!rxexecute(f->curtext, nil, p, r.q1, &sel)){ /* no match, but y should still run */
81159cc4ca5SDavid du Colombier 			if(xy || op>r.q1)
81259cc4ca5SDavid du Colombier 				break;
81359cc4ca5SDavid du Colombier 			tr.q0 = op, tr.q1 = r.q1;
81459cc4ca5SDavid du Colombier 			p = r.q1+1;	/* exit next loop */
81559cc4ca5SDavid du Colombier 		}else{
81659cc4ca5SDavid du Colombier 			if(sel.r[0].q0==sel.r[0].q1){	/* empty match? */
81759cc4ca5SDavid du Colombier 				if(sel.r[0].q0==op){
81859cc4ca5SDavid du Colombier 					p++;
81959cc4ca5SDavid du Colombier 					continue;
82059cc4ca5SDavid du Colombier 				}
82159cc4ca5SDavid du Colombier 				p = sel.r[0].q1+1;
82259cc4ca5SDavid du Colombier 			}else
82359cc4ca5SDavid du Colombier 				p = sel.r[0].q1;
82459cc4ca5SDavid du Colombier 			if(xy)
82559cc4ca5SDavid du Colombier 				tr = sel.r[0];
82659cc4ca5SDavid du Colombier 			else
82759cc4ca5SDavid du Colombier 				tr.q0 = op, tr.q1 = sel.r[0].q0;
82859cc4ca5SDavid du Colombier 		}
82980ee5cbfSDavid du Colombier 		op = sel.r[0].q1;
83059cc4ca5SDavid du Colombier 		nrp++;
83159cc4ca5SDavid du Colombier 		rp = erealloc(rp, nrp*sizeof(Range));
83259cc4ca5SDavid du Colombier 		rp[nrp-1] = tr;
83359cc4ca5SDavid du Colombier 	}
83459cc4ca5SDavid du Colombier 	loopcmd(f, cp->cmd, rp, nrp);
83559cc4ca5SDavid du Colombier 	free(rp);
83659cc4ca5SDavid du Colombier 	--nest;
83759cc4ca5SDavid du Colombier }
83859cc4ca5SDavid du Colombier 
83959cc4ca5SDavid du Colombier void
linelooper(File * f,Cmd * cp)84059cc4ca5SDavid du Colombier linelooper(File *f, Cmd *cp)
84159cc4ca5SDavid du Colombier {
84259cc4ca5SDavid du Colombier 	long nrp, p;
84359cc4ca5SDavid du Colombier 	Range r, linesel;
84459cc4ca5SDavid du Colombier 	Address a, a3;
84559cc4ca5SDavid du Colombier 	Range *rp;
84659cc4ca5SDavid du Colombier 
84759cc4ca5SDavid du Colombier 	nest++;
84859cc4ca5SDavid du Colombier 	nrp = 0;
84959cc4ca5SDavid du Colombier 	rp = nil;
85059cc4ca5SDavid du Colombier 	r = addr.r;
85159cc4ca5SDavid du Colombier 	a3.f = f;
85259cc4ca5SDavid du Colombier 	a3.r.q0 = a3.r.q1 = r.q0;
85359cc4ca5SDavid du Colombier 	a = lineaddr(0, a3, 1);
85459cc4ca5SDavid du Colombier 	linesel = a.r;
85559cc4ca5SDavid du Colombier 	for(p = r.q0; p<r.q1; p = a3.r.q1){
85659cc4ca5SDavid du Colombier 		a3.r.q0 = a3.r.q1;
85759cc4ca5SDavid du Colombier 		if(p!=r.q0 || linesel.q1==p){
85859cc4ca5SDavid du Colombier 			a = lineaddr(1, a3, 1);
85959cc4ca5SDavid du Colombier 			linesel = a.r;
86059cc4ca5SDavid du Colombier 		}
86159cc4ca5SDavid du Colombier 		if(linesel.q0 >= r.q1)
86259cc4ca5SDavid du Colombier 			break;
86359cc4ca5SDavid du Colombier 		if(linesel.q1 >= r.q1)
86459cc4ca5SDavid du Colombier 			linesel.q1 = r.q1;
86559cc4ca5SDavid du Colombier 		if(linesel.q1 > linesel.q0)
86659cc4ca5SDavid du Colombier 			if(linesel.q0>=a3.r.q1 && linesel.q1>a3.r.q1){
86759cc4ca5SDavid du Colombier 				a3.r = linesel;
86859cc4ca5SDavid du Colombier 				nrp++;
86959cc4ca5SDavid du Colombier 				rp = erealloc(rp, nrp*sizeof(Range));
87059cc4ca5SDavid du Colombier 				rp[nrp-1] = linesel;
87159cc4ca5SDavid du Colombier 				continue;
87259cc4ca5SDavid du Colombier 			}
87359cc4ca5SDavid du Colombier 		break;
87459cc4ca5SDavid du Colombier 	}
87559cc4ca5SDavid du Colombier 	loopcmd(f, cp->cmd, rp, nrp);
87659cc4ca5SDavid du Colombier 	free(rp);
87759cc4ca5SDavid du Colombier 	--nest;
87859cc4ca5SDavid du Colombier }
87959cc4ca5SDavid du Colombier 
88059cc4ca5SDavid du Colombier struct Looper
88159cc4ca5SDavid du Colombier {
88259cc4ca5SDavid du Colombier 	Cmd *cp;
88359cc4ca5SDavid du Colombier 	int	XY;
88459cc4ca5SDavid du Colombier 	Window	**w;
88559cc4ca5SDavid du Colombier 	int	nw;
88659cc4ca5SDavid du Colombier } loopstruct;	/* only one; X and Y can't nest */
88759cc4ca5SDavid du Colombier 
88859cc4ca5SDavid du Colombier void
alllooper(Window * w,void * v)88959cc4ca5SDavid du Colombier alllooper(Window *w, void *v)
89059cc4ca5SDavid du Colombier {
89159cc4ca5SDavid du Colombier 	Text *t;
89259cc4ca5SDavid du Colombier 	struct Looper *lp;
89359cc4ca5SDavid du Colombier 	Cmd *cp;
89459cc4ca5SDavid du Colombier 
89559cc4ca5SDavid du Colombier 	lp = v;
89659cc4ca5SDavid du Colombier 	cp = lp->cp;
89759cc4ca5SDavid du Colombier //	if(w->isscratch || w->isdir)
89859cc4ca5SDavid du Colombier //		return;
89959cc4ca5SDavid du Colombier 	t = &w->body;
90059cc4ca5SDavid du Colombier 	/* only use this window if it's the current window for the file */
90159cc4ca5SDavid du Colombier 	if(t->file->curtext != t)
90259cc4ca5SDavid du Colombier 		return;
90359cc4ca5SDavid du Colombier //	if(w->nopen[QWevent] > 0)
90459cc4ca5SDavid du Colombier //		return;
90559cc4ca5SDavid du Colombier 	/* no auto-execute on files without names */
90659cc4ca5SDavid du Colombier 	if(cp->re==nil && t->file->nname==0)
90759cc4ca5SDavid du Colombier 		return;
90859cc4ca5SDavid du Colombier 	if(cp->re==nil || filematch(t->file, cp->re)==lp->XY){
90959cc4ca5SDavid du Colombier 		lp->w = erealloc(lp->w, (lp->nw+1)*sizeof(Window*));
91059cc4ca5SDavid du Colombier 		lp->w[lp->nw++] = w;
91159cc4ca5SDavid du Colombier 	}
91259cc4ca5SDavid du Colombier }
91359cc4ca5SDavid du Colombier 
91459cc4ca5SDavid du Colombier void
alllocker(Window * w,void * v)915e288d156SDavid du Colombier alllocker(Window *w, void *v)
916e288d156SDavid du Colombier {
917e288d156SDavid du Colombier 	if(v)
918e288d156SDavid du Colombier 		incref(w);
919e288d156SDavid du Colombier 	else
920e288d156SDavid du Colombier 		winclose(w);
921e288d156SDavid du Colombier }
922e288d156SDavid du Colombier 
923e288d156SDavid du Colombier void
filelooper(Cmd * cp,int XY)92459cc4ca5SDavid du Colombier filelooper(Cmd *cp, int XY)
92559cc4ca5SDavid du Colombier {
92659cc4ca5SDavid du Colombier 	int i;
92759cc4ca5SDavid du Colombier 
92859cc4ca5SDavid du Colombier 	if(Glooping++)
92959cc4ca5SDavid du Colombier 		editerror("can't nest %c command", "YX"[XY]);
93059cc4ca5SDavid du Colombier 	nest++;
93159cc4ca5SDavid du Colombier 
93259cc4ca5SDavid du Colombier 	loopstruct.cp = cp;
93359cc4ca5SDavid du Colombier 	loopstruct.XY = XY;
93459cc4ca5SDavid du Colombier 	if(loopstruct.w)	/* error'ed out last time */
93559cc4ca5SDavid du Colombier 		free(loopstruct.w);
93659cc4ca5SDavid du Colombier 	loopstruct.w = nil;
93759cc4ca5SDavid du Colombier 	loopstruct.nw = 0;
93859cc4ca5SDavid du Colombier 	allwindows(alllooper, &loopstruct);
939e288d156SDavid du Colombier 	/*
940e288d156SDavid du Colombier 	 * add a ref to all windows to keep safe windows accessed by X
941e288d156SDavid du Colombier 	 * that would not otherwise have a ref to hold them up during
94206300895SDavid du Colombier 	 * the shenanigans.  note this with globalincref so that any
94306300895SDavid du Colombier 	 * newly created windows start with an extra reference.
944e288d156SDavid du Colombier 	 */
945e288d156SDavid du Colombier 	allwindows(alllocker, (void*)1);
94606300895SDavid du Colombier 	globalincref = 1;
94759cc4ca5SDavid du Colombier 	for(i=0; i<loopstruct.nw; i++)
94859cc4ca5SDavid du Colombier 		cmdexec(&loopstruct.w[i]->body, cp->cmd);
949e288d156SDavid du Colombier 	allwindows(alllocker, (void*)0);
95006300895SDavid du Colombier 	globalincref = 0;
95159cc4ca5SDavid du Colombier 	free(loopstruct.w);
95259cc4ca5SDavid du Colombier 	loopstruct.w = nil;
95359cc4ca5SDavid du Colombier 
95459cc4ca5SDavid du Colombier 	--Glooping;
95559cc4ca5SDavid du Colombier 	--nest;
95659cc4ca5SDavid du Colombier }
95759cc4ca5SDavid du Colombier 
95859cc4ca5SDavid du Colombier void
nextmatch(File * f,String * r,long p,int sign)95959cc4ca5SDavid du Colombier nextmatch(File *f, String *r, long p, int sign)
96059cc4ca5SDavid du Colombier {
96180ee5cbfSDavid du Colombier 	if(rxcompile(r->r) == FALSE)
96280ee5cbfSDavid du Colombier 		editerror("bad regexp in command address");
96359cc4ca5SDavid du Colombier 	if(sign >= 0){
96459cc4ca5SDavid du Colombier 		if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
96559cc4ca5SDavid du Colombier 			editerror("no match for regexp");
96659cc4ca5SDavid du Colombier 		if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q0==p){
96759cc4ca5SDavid du Colombier 			if(++p>f->nc)
96859cc4ca5SDavid du Colombier 				p = 0;
96959cc4ca5SDavid du Colombier 			if(!rxexecute(f->curtext, nil, p, 0x7FFFFFFFL, &sel))
97080ee5cbfSDavid du Colombier 				editerror("address");
97159cc4ca5SDavid du Colombier 		}
97259cc4ca5SDavid du Colombier 	}else{
97359cc4ca5SDavid du Colombier 		if(!rxbexecute(f->curtext, p, &sel))
97459cc4ca5SDavid du Colombier 			editerror("no match for regexp");
97559cc4ca5SDavid du Colombier 		if(sel.r[0].q0==sel.r[0].q1 && sel.r[0].q1==p){
97659cc4ca5SDavid du Colombier 			if(--p<0)
97759cc4ca5SDavid du Colombier 				p = f->nc;
97859cc4ca5SDavid du Colombier 			if(!rxbexecute(f->curtext, p, &sel))
97980ee5cbfSDavid du Colombier 				editerror("address");
98059cc4ca5SDavid du Colombier 		}
98159cc4ca5SDavid du Colombier 	}
98259cc4ca5SDavid du Colombier }
98359cc4ca5SDavid du Colombier 
98459cc4ca5SDavid du Colombier File	*matchfile(String*);
98559cc4ca5SDavid du Colombier Address	charaddr(long, Address, int);
98659cc4ca5SDavid du Colombier Address	lineaddr(long, Address, int);
98759cc4ca5SDavid du Colombier 
98859cc4ca5SDavid du Colombier Address
cmdaddress(Addr * ap,Address a,int sign)98959cc4ca5SDavid du Colombier cmdaddress(Addr *ap, Address a, int sign)
99059cc4ca5SDavid du Colombier {
99159cc4ca5SDavid du Colombier 	File *f = a.f;
99259cc4ca5SDavid du Colombier 	Address a1, a2;
99359cc4ca5SDavid du Colombier 
99459cc4ca5SDavid du Colombier 	do{
99559cc4ca5SDavid du Colombier 		switch(ap->type){
99659cc4ca5SDavid du Colombier 		case 'l':
99759cc4ca5SDavid du Colombier 		case '#':
99859cc4ca5SDavid du Colombier 			a = (*(ap->type=='#'?charaddr:lineaddr))(ap->num, a, sign);
99959cc4ca5SDavid du Colombier 			break;
100059cc4ca5SDavid du Colombier 
100159cc4ca5SDavid du Colombier 		case '.':
100259cc4ca5SDavid du Colombier 			mkaddr(&a, f);
100359cc4ca5SDavid du Colombier 			break;
100459cc4ca5SDavid du Colombier 
100559cc4ca5SDavid du Colombier 		case '$':
100659cc4ca5SDavid du Colombier 			a.r.q0 = a.r.q1 = f->nc;
100759cc4ca5SDavid du Colombier 			break;
100859cc4ca5SDavid du Colombier 
100959cc4ca5SDavid du Colombier 		case '\'':
101059cc4ca5SDavid du Colombier editerror("can't handle '");
101159cc4ca5SDavid du Colombier //			a.r = f->mark;
101259cc4ca5SDavid du Colombier 			break;
101359cc4ca5SDavid du Colombier 
101459cc4ca5SDavid du Colombier 		case '?':
101559cc4ca5SDavid du Colombier 			sign = -sign;
101659cc4ca5SDavid du Colombier 			if(sign == 0)
101759cc4ca5SDavid du Colombier 				sign = -1;
101859cc4ca5SDavid du Colombier 			/* fall through */
101959cc4ca5SDavid du Colombier 		case '/':
102059cc4ca5SDavid du Colombier 			nextmatch(f, ap->re, sign>=0? a.r.q1 : a.r.q0, sign);
102159cc4ca5SDavid du Colombier 			a.r = sel.r[0];
102259cc4ca5SDavid du Colombier 			break;
102359cc4ca5SDavid du Colombier 
102459cc4ca5SDavid du Colombier 		case '"':
102559cc4ca5SDavid du Colombier 			f = matchfile(ap->re);
102659cc4ca5SDavid du Colombier 			mkaddr(&a, f);
102759cc4ca5SDavid du Colombier 			break;
102859cc4ca5SDavid du Colombier 
102959cc4ca5SDavid du Colombier 		case '*':
103059cc4ca5SDavid du Colombier 			a.r.q0 = 0, a.r.q1 = f->nc;
103159cc4ca5SDavid du Colombier 			return a;
103259cc4ca5SDavid du Colombier 
103359cc4ca5SDavid du Colombier 		case ',':
103459cc4ca5SDavid du Colombier 		case ';':
103559cc4ca5SDavid du Colombier 			if(ap->left)
103659cc4ca5SDavid du Colombier 				a1 = cmdaddress(ap->left, a, 0);
103759cc4ca5SDavid du Colombier 			else
103859cc4ca5SDavid du Colombier 				a1.f = a.f, a1.r.q0 = a1.r.q1 = 0;
103959cc4ca5SDavid du Colombier 			if(ap->type == ';'){
104059cc4ca5SDavid du Colombier 				f = a1.f;
104159cc4ca5SDavid du Colombier 				a = a1;
104259cc4ca5SDavid du Colombier 				f->curtext->q0 = a1.r.q0;
104359cc4ca5SDavid du Colombier 				f->curtext->q1 = a1.r.q1;
104459cc4ca5SDavid du Colombier 			}
104559cc4ca5SDavid du Colombier 			if(ap->next)
104659cc4ca5SDavid du Colombier 				a2 = cmdaddress(ap->next, a, 0);
104759cc4ca5SDavid du Colombier 			else
104859cc4ca5SDavid du Colombier 				a2.f = a.f, a2.r.q0 = a2.r.q1 = f->nc;
104959cc4ca5SDavid du Colombier 			if(a1.f != a2.f)
105059cc4ca5SDavid du Colombier 				editerror("addresses in different files");
105159cc4ca5SDavid du Colombier 			a.f = a1.f, a.r.q0 = a1.r.q0, a.r.q1 = a2.r.q1;
105259cc4ca5SDavid du Colombier 			if(a.r.q1 < a.r.q0)
105359cc4ca5SDavid du Colombier 				editerror("addresses out of order");
105459cc4ca5SDavid du Colombier 			return a;
105559cc4ca5SDavid du Colombier 
105659cc4ca5SDavid du Colombier 		case '+':
105759cc4ca5SDavid du Colombier 		case '-':
105859cc4ca5SDavid du Colombier 			sign = 1;
105959cc4ca5SDavid du Colombier 			if(ap->type == '-')
106059cc4ca5SDavid du Colombier 				sign = -1;
106159cc4ca5SDavid du Colombier 			if(ap->next==0 || ap->next->type=='+' || ap->next->type=='-')
106259cc4ca5SDavid du Colombier 				a = lineaddr(1L, a, sign);
106359cc4ca5SDavid du Colombier 			break;
106459cc4ca5SDavid du Colombier 		default:
106559cc4ca5SDavid du Colombier 			error("cmdaddress");
106659cc4ca5SDavid du Colombier 			return a;
106759cc4ca5SDavid du Colombier 		}
106859cc4ca5SDavid du Colombier 	}while(ap = ap->next);	/* assign = */
106959cc4ca5SDavid du Colombier 	return a;
107059cc4ca5SDavid du Colombier }
107159cc4ca5SDavid du Colombier 
107259cc4ca5SDavid du Colombier struct Tofile{
107359cc4ca5SDavid du Colombier 	File		*f;
107459cc4ca5SDavid du Colombier 	String	*r;
107559cc4ca5SDavid du Colombier };
107659cc4ca5SDavid du Colombier 
107759cc4ca5SDavid du Colombier void
alltofile(Window * w,void * v)107859cc4ca5SDavid du Colombier alltofile(Window *w, void *v)
107959cc4ca5SDavid du Colombier {
108059cc4ca5SDavid du Colombier 	Text *t;
108159cc4ca5SDavid du Colombier 	struct Tofile *tp;
108259cc4ca5SDavid du Colombier 
108359cc4ca5SDavid du Colombier 	tp = v;
108459cc4ca5SDavid du Colombier 	if(tp->f != nil)
108559cc4ca5SDavid du Colombier 		return;
108659cc4ca5SDavid du Colombier 	if(w->isscratch || w->isdir)
108759cc4ca5SDavid du Colombier 		return;
108859cc4ca5SDavid du Colombier 	t = &w->body;
108959cc4ca5SDavid du Colombier 	/* only use this window if it's the current window for the file */
109059cc4ca5SDavid du Colombier 	if(t->file->curtext != t)
109159cc4ca5SDavid du Colombier 		return;
109259cc4ca5SDavid du Colombier //	if(w->nopen[QWevent] > 0)
109359cc4ca5SDavid du Colombier //		return;
109459cc4ca5SDavid du Colombier 	if(runeeq(tp->r->r, tp->r->n, t->file->name, t->file->nname))
109559cc4ca5SDavid du Colombier 		tp->f = t->file;
109659cc4ca5SDavid du Colombier }
109759cc4ca5SDavid du Colombier 
109859cc4ca5SDavid du Colombier File*
tofile(String * r)109959cc4ca5SDavid du Colombier tofile(String *r)
110059cc4ca5SDavid du Colombier {
110159cc4ca5SDavid du Colombier 	struct Tofile t;
110259cc4ca5SDavid du Colombier 	String rr;
110359cc4ca5SDavid du Colombier 
110459cc4ca5SDavid du Colombier 	rr.r = skipbl(r->r, r->n, &rr.n);
110559cc4ca5SDavid du Colombier 	t.f = nil;
110659cc4ca5SDavid du Colombier 	t.r = &rr;
110759cc4ca5SDavid du Colombier 	allwindows(alltofile, &t);
110859cc4ca5SDavid du Colombier 	if(t.f == nil)
110959cc4ca5SDavid du Colombier 		editerror("no such file\"%S\"", rr.r);
111059cc4ca5SDavid du Colombier 	return t.f;
111159cc4ca5SDavid du Colombier }
111259cc4ca5SDavid du Colombier 
111359cc4ca5SDavid du Colombier void
allmatchfile(Window * w,void * v)111459cc4ca5SDavid du Colombier allmatchfile(Window *w, void *v)
111559cc4ca5SDavid du Colombier {
111659cc4ca5SDavid du Colombier 	struct Tofile *tp;
111759cc4ca5SDavid du Colombier 	Text *t;
111859cc4ca5SDavid du Colombier 
111959cc4ca5SDavid du Colombier 	tp = v;
112059cc4ca5SDavid du Colombier 	if(w->isscratch || w->isdir)
112159cc4ca5SDavid du Colombier 		return;
112259cc4ca5SDavid du Colombier 	t = &w->body;
112359cc4ca5SDavid du Colombier 	/* only use this window if it's the current window for the file */
112459cc4ca5SDavid du Colombier 	if(t->file->curtext != t)
112559cc4ca5SDavid du Colombier 		return;
112659cc4ca5SDavid du Colombier //	if(w->nopen[QWevent] > 0)
112759cc4ca5SDavid du Colombier //		return;
112859cc4ca5SDavid du Colombier 	if(filematch(w->body.file, tp->r)){
112959cc4ca5SDavid du Colombier 		if(tp->f != nil)
113059cc4ca5SDavid du Colombier 			editerror("too many files match \"%S\"", tp->r->r);
113159cc4ca5SDavid du Colombier 		tp->f = w->body.file;
113259cc4ca5SDavid du Colombier 	}
113359cc4ca5SDavid du Colombier }
113459cc4ca5SDavid du Colombier 
113559cc4ca5SDavid du Colombier File*
matchfile(String * r)113659cc4ca5SDavid du Colombier matchfile(String *r)
113759cc4ca5SDavid du Colombier {
113859cc4ca5SDavid du Colombier 	struct Tofile tf;
113959cc4ca5SDavid du Colombier 
114059cc4ca5SDavid du Colombier 	tf.f = nil;
114159cc4ca5SDavid du Colombier 	tf.r = r;
114259cc4ca5SDavid du Colombier 	allwindows(allmatchfile, &tf);
114359cc4ca5SDavid du Colombier 
114459cc4ca5SDavid du Colombier 	if(tf.f == nil)
114559cc4ca5SDavid du Colombier 		editerror("no file matches \"%S\"", r->r);
114659cc4ca5SDavid du Colombier 	return tf.f;
114759cc4ca5SDavid du Colombier }
114859cc4ca5SDavid du Colombier 
114959cc4ca5SDavid du Colombier int
filematch(File * f,String * r)115059cc4ca5SDavid du Colombier filematch(File *f, String *r)
115159cc4ca5SDavid du Colombier {
115259cc4ca5SDavid du Colombier 	char *buf;
115359cc4ca5SDavid du Colombier 	Rune *rbuf;
115480ee5cbfSDavid du Colombier 	Window *w;
115580ee5cbfSDavid du Colombier 	int match, i, dirty;
115659cc4ca5SDavid du Colombier 	Rangeset s;
115759cc4ca5SDavid du Colombier 
115880ee5cbfSDavid du Colombier 	/* compile expr first so if we get an error, we haven't allocated anything */
115980ee5cbfSDavid du Colombier 	if(rxcompile(r->r) == FALSE)
116080ee5cbfSDavid du Colombier 		editerror("bad regexp in file match");
116159cc4ca5SDavid du Colombier 	buf = fbufalloc();
116280ee5cbfSDavid du Colombier 	w = f->curtext->w;
116380ee5cbfSDavid du Colombier 	/* same check for dirty as in settag, but we know ncache==0 */
116480ee5cbfSDavid du Colombier 	dirty = !w->isdir && !w->isscratch && f->mod;
116580ee5cbfSDavid du Colombier 	snprint(buf, BUFSIZE, "%c%c%c %.*S\n", " '"[dirty],
116659cc4ca5SDavid du Colombier 		'+', " ."[curtext!=nil && curtext->file==f], f->nname, f->name);
116759cc4ca5SDavid du Colombier 	rbuf = bytetorune(buf, &i);
116859cc4ca5SDavid du Colombier 	fbuffree(buf);
116959cc4ca5SDavid du Colombier 	match = rxexecute(nil, rbuf, 0, i, &s);
117059cc4ca5SDavid du Colombier 	free(rbuf);
117159cc4ca5SDavid du Colombier 	return match;
117259cc4ca5SDavid du Colombier }
117359cc4ca5SDavid du Colombier 
117459cc4ca5SDavid du Colombier Address
charaddr(long l,Address addr,int sign)117559cc4ca5SDavid du Colombier charaddr(long l, Address addr, int sign)
117659cc4ca5SDavid du Colombier {
117759cc4ca5SDavid du Colombier 	if(sign == 0)
117859cc4ca5SDavid du Colombier 		addr.r.q0 = addr.r.q1 = l;
117959cc4ca5SDavid du Colombier 	else if(sign < 0)
118059cc4ca5SDavid du Colombier 		addr.r.q1 = addr.r.q0 -= l;
118159cc4ca5SDavid du Colombier 	else if(sign > 0)
118259cc4ca5SDavid du Colombier 		addr.r.q0 = addr.r.q1 += l;
118359cc4ca5SDavid du Colombier 	if(addr.r.q0<0 || addr.r.q1>addr.f->nc)
118459cc4ca5SDavid du Colombier 		editerror("address out of range");
118559cc4ca5SDavid du Colombier 	return addr;
118659cc4ca5SDavid du Colombier }
118759cc4ca5SDavid du Colombier 
118859cc4ca5SDavid du Colombier Address
lineaddr(long l,Address addr,int sign)118959cc4ca5SDavid du Colombier lineaddr(long l, Address addr, int sign)
119059cc4ca5SDavid du Colombier {
119159cc4ca5SDavid du Colombier 	int n;
119259cc4ca5SDavid du Colombier 	int c;
119359cc4ca5SDavid du Colombier 	File *f = addr.f;
119459cc4ca5SDavid du Colombier 	Address a;
119559cc4ca5SDavid du Colombier 	long p;
119659cc4ca5SDavid du Colombier 
119759cc4ca5SDavid du Colombier 	a.f = f;
119859cc4ca5SDavid du Colombier 	if(sign >= 0){
119959cc4ca5SDavid du Colombier 		if(l == 0){
120059cc4ca5SDavid du Colombier 			if(sign==0 || addr.r.q1==0){
120159cc4ca5SDavid du Colombier 				a.r.q0 = a.r.q1 = 0;
120259cc4ca5SDavid du Colombier 				return a;
120359cc4ca5SDavid du Colombier 			}
120459cc4ca5SDavid du Colombier 			a.r.q0 = addr.r.q1;
120559cc4ca5SDavid du Colombier 			p = addr.r.q1-1;
120659cc4ca5SDavid du Colombier 		}else{
120759cc4ca5SDavid du Colombier 			if(sign==0 || addr.r.q1==0){
120859cc4ca5SDavid du Colombier 				p = 0;
120959cc4ca5SDavid du Colombier 				n = 1;
121059cc4ca5SDavid du Colombier 			}else{
121159cc4ca5SDavid du Colombier 				p = addr.r.q1-1;
121259cc4ca5SDavid du Colombier 				n = textreadc(f->curtext, p++)=='\n';
121359cc4ca5SDavid du Colombier 			}
121459cc4ca5SDavid du Colombier 			while(n < l){
121559cc4ca5SDavid du Colombier 				if(p >= f->nc)
121659cc4ca5SDavid du Colombier 					editerror("address out of range");
121759cc4ca5SDavid du Colombier 				if(textreadc(f->curtext, p++) == '\n')
121859cc4ca5SDavid du Colombier 					n++;
121959cc4ca5SDavid du Colombier 			}
122059cc4ca5SDavid du Colombier 			a.r.q0 = p;
122159cc4ca5SDavid du Colombier 		}
122259cc4ca5SDavid du Colombier 		while(p < f->nc && textreadc(f->curtext, p++)!='\n')
122359cc4ca5SDavid du Colombier 			;
122459cc4ca5SDavid du Colombier 		a.r.q1 = p;
122559cc4ca5SDavid du Colombier 	}else{
122659cc4ca5SDavid du Colombier 		p = addr.r.q0;
122759cc4ca5SDavid du Colombier 		if(l == 0)
122859cc4ca5SDavid du Colombier 			a.r.q1 = addr.r.q0;
122959cc4ca5SDavid du Colombier 		else{
123059cc4ca5SDavid du Colombier 			for(n = 0; n<l; ){	/* always runs once */
123159cc4ca5SDavid du Colombier 				if(p == 0){
123259cc4ca5SDavid du Colombier 					if(++n != l)
123359cc4ca5SDavid du Colombier 						editerror("address out of range");
123459cc4ca5SDavid du Colombier 				}else{
123559cc4ca5SDavid du Colombier 					c = textreadc(f->curtext, p-1);
123659cc4ca5SDavid du Colombier 					if(c != '\n' || ++n != l)
123759cc4ca5SDavid du Colombier 						p--;
123859cc4ca5SDavid du Colombier 				}
123959cc4ca5SDavid du Colombier 			}
124059cc4ca5SDavid du Colombier 			a.r.q1 = p;
124159cc4ca5SDavid du Colombier 			if(p > 0)
124259cc4ca5SDavid du Colombier 				p--;
124359cc4ca5SDavid du Colombier 		}
124459cc4ca5SDavid du Colombier 		while(p > 0 && textreadc(f->curtext, p-1)!='\n')	/* lines start after a newline */
124559cc4ca5SDavid du Colombier 			p--;
124659cc4ca5SDavid du Colombier 		a.r.q0 = p;
124759cc4ca5SDavid du Colombier 	}
124859cc4ca5SDavid du Colombier 	return a;
124959cc4ca5SDavid du Colombier }
125059cc4ca5SDavid du Colombier 
125159cc4ca5SDavid du Colombier struct Filecheck
125259cc4ca5SDavid du Colombier {
125359cc4ca5SDavid du Colombier 	File	*f;
125459cc4ca5SDavid du Colombier 	Rune	*r;
125559cc4ca5SDavid du Colombier 	int nr;
125659cc4ca5SDavid du Colombier };
125759cc4ca5SDavid du Colombier 
125859cc4ca5SDavid du Colombier void
allfilecheck(Window * w,void * v)125959cc4ca5SDavid du Colombier allfilecheck(Window *w, void *v)
126059cc4ca5SDavid du Colombier {
126159cc4ca5SDavid du Colombier 	struct Filecheck *fp;
126259cc4ca5SDavid du Colombier 	File *f;
126359cc4ca5SDavid du Colombier 
126459cc4ca5SDavid du Colombier 	fp = v;
126559cc4ca5SDavid du Colombier 	f = w->body.file;
126659cc4ca5SDavid du Colombier 	if(w->body.file == fp->f)
126759cc4ca5SDavid du Colombier 		return;
126859cc4ca5SDavid du Colombier 	if(runeeq(fp->r, fp->nr, f->name, f->nname))
126959cc4ca5SDavid du Colombier 		warning(nil, "warning: duplicate file name \"%.*S\"\n", fp->nr, fp->r);
127059cc4ca5SDavid du Colombier }
127159cc4ca5SDavid du Colombier 
127259cc4ca5SDavid du Colombier Rune*
cmdname(File * f,String * str,int set)127359cc4ca5SDavid du Colombier cmdname(File *f, String *str, int set)
127459cc4ca5SDavid du Colombier {
127559cc4ca5SDavid du Colombier 	Rune *r, *s;
127659cc4ca5SDavid du Colombier 	int n;
127759cc4ca5SDavid du Colombier 	struct Filecheck fc;
127859cc4ca5SDavid du Colombier 	Runestr newname;
127959cc4ca5SDavid du Colombier 
128059cc4ca5SDavid du Colombier 	r = nil;
128159cc4ca5SDavid du Colombier 	n = str->n;
128259cc4ca5SDavid du Colombier 	s = str->r;
128359cc4ca5SDavid du Colombier 	if(n == 0){
128459cc4ca5SDavid du Colombier 		/* no name; use existing */
128559cc4ca5SDavid du Colombier 		if(f->nname == 0)
128659cc4ca5SDavid du Colombier 			return nil;
128759cc4ca5SDavid du Colombier 		r = runemalloc(f->nname+1);
128859cc4ca5SDavid du Colombier 		runemove(r, f->name, f->nname);
128959cc4ca5SDavid du Colombier 		return r;
129059cc4ca5SDavid du Colombier 	}
129159cc4ca5SDavid du Colombier 	s = skipbl(s, n, &n);
129259cc4ca5SDavid du Colombier 	if(n == 0)
129359cc4ca5SDavid du Colombier 		goto Return;
129459cc4ca5SDavid du Colombier 
129559cc4ca5SDavid du Colombier 	if(s[0] == '/'){
129659cc4ca5SDavid du Colombier 		r = runemalloc(n+1);
129759cc4ca5SDavid du Colombier 		runemove(r, s, n);
129859cc4ca5SDavid du Colombier 	}else{
129959cc4ca5SDavid du Colombier 		newname = dirname(f->curtext, runestrdup(s), n);
130059cc4ca5SDavid du Colombier 		n = newname.nr;
13015ed2c76cSDavid du Colombier 		r = runemalloc(n+1);	/* NUL terminate */
13025ed2c76cSDavid du Colombier 		runemove(r, newname.r, n);
13035ed2c76cSDavid du Colombier 		free(newname.r);
130459cc4ca5SDavid du Colombier 	}
130559cc4ca5SDavid du Colombier 	fc.f = f;
130659cc4ca5SDavid du Colombier 	fc.r = r;
130759cc4ca5SDavid du Colombier 	fc.nr = n;
130859cc4ca5SDavid du Colombier 	allwindows(allfilecheck, &fc);
130959cc4ca5SDavid du Colombier 	if(f->nname == 0)
131059cc4ca5SDavid du Colombier 		set = TRUE;
131159cc4ca5SDavid du Colombier 
131259cc4ca5SDavid du Colombier     Return:
131359cc4ca5SDavid du Colombier 	if(set && !runeeq(r, n, f->name, f->nname)){
131459cc4ca5SDavid du Colombier 		filemark(f);
131559cc4ca5SDavid du Colombier 		f->mod = TRUE;
131659cc4ca5SDavid du Colombier 		f->curtext->w->dirty = TRUE;
131759cc4ca5SDavid du Colombier 		winsetname(f->curtext->w, r, n);
131859cc4ca5SDavid du Colombier 	}
131959cc4ca5SDavid du Colombier 	return r;
132059cc4ca5SDavid du Colombier }
1321