1329 lines · 24.1 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <draw.h>#include <thread.h>#include <cursor.h>#include <mouse.h>#include <keyboard.h>#include <frame.h>#include <fcall.h>#include <plumb.h>#include "dat.h"#include "edit.h"#include "fns.h" int	Glooping;int	nest;char	Enoname[] = "no file name given"; Address	addr;File	*menu;Rangeset	sel;extern	Text*	curtext;Rune	*collection;int	ncollection; int	append(File*, Cmd*, long);int	pdisplay(File*);void	pfilename(File*);void	looper(File*, Cmd*, int);void	filelooper(Cmd*, int);void	linelooper(File*, Cmd*);Address	lineaddr(long, Address, int);int	filematch(File*, String*);File	*tofile(String*);Rune*	cmdname(File *f, String *s, int);void	runpipe(Text*, int, Rune*, int, int); voidclearcollection(void){	free(collection);	collection = nil;	ncollection = 0;} voidresetxec(void){	Glooping = nest = 0;	clearcollection();} voidmkaddr(Address *a, File *f){	a->r.q0 = f->curtext->q0;	a->r.q1 = f->curtext->q1;	a->f = f;} intcmdexec(Text *t, Cmd *cp){	int i;	Addr *ap;	File *f;	Window *w;	Address dot; 	if(t == nil)		w = nil;	else		w = t->w;	if(w==nil && (cp->addr==0 || cp->addr->type!='"') &&	    !utfrune("bBnqUXY!", cp->cmdc) &&	    !(cp->cmdc=='D' && cp->text))		editerror("no current window");	i = cmdlookup(cp->cmdc);	/* will be -1 for '{' */	f = nil;	if(t && t->w){		t = &t->w->body;		f = t->file;		f->curtext = t;	}	if(i>=0 && cmdtab[i].defaddr != aNo){		if((ap=cp->addr)==0 && cp->cmdc!='\n'){			cp->addr = ap = newaddr();			ap->type = '.';			if(cmdtab[i].defaddr == aAll)				ap->type = '*';		}else if(ap && ap->type=='"' && ap->next==0 && cp->cmdc!='\n'){			ap->next = newaddr();			ap->next->type = '.';			if(cmdtab[i].defaddr == aAll)				ap->next->type = '*';		}		if(cp->addr){	/* may be false for '\n' (only) */			static Address none = {0,0,nil};			if(f){				mkaddr(&dot, f);				addr = cmdaddress(ap, dot, 0);			}else	/* a " */				addr = cmdaddress(ap, none, 0);			f = addr.f;			t = f->curtext;		}	}	switch(cp->cmdc){	case '{':		mkaddr(&dot, f);		if(cp->addr != nil)			dot = cmdaddress(cp->addr, dot, 0);		for(cp = cp->cmd; cp; cp = cp->next){			if(dot.r.q1 > t->file->nc)				editerror("dot extends past end of buffer during { command");			t->q0 = dot.r.q0;			t->q1 = dot.r.q1;			cmdexec(t, cp);		}		break;	default:		if(i < 0)			editerror("unknown command %c in cmdexec", cp->cmdc);		i = (*cmdtab[i].fn)(t, cp);		return i;	}	return 1;} char*edittext(Window *w, int q, Rune *r, int nr){	File *f; 	f = w->body.file;	switch(editing){	case Inactive:		return "permission denied";	case Inserting:		eloginsert(f, q, r, nr);		return nil;	case Collecting:		collection = runerealloc(collection, ncollection+nr+1);		runemove(collection+ncollection, r, nr);		ncollection += nr;		collection[ncollection] = '\0';		return nil;	default:		return "unknown state in edittext";	}} /* string is known to be NUL-terminated */Rune*filelist(Text *t, Rune *r, int nr){	if(nr == 0)		return nil;	r = skipbl(r, nr, &nr);	if(r[0] != '<')		return runestrdup(r);	/* use < command to collect text */	clearcollection();	runpipe(t, '<', r+1, nr-1, Collecting);	return collection;} inta_cmd(Text *t, Cmd *cp){	return append(t->file, cp, addr.r.q1);} intb_cmd(Text*, Cmd *cp){	File *f; 	f = tofile(cp->text);	if(nest == 0)		pfilename(f);	curtext = f->curtext;	return TRUE;} intB_cmd(Text *t, Cmd *cp){	Rune *list, *r, *s;	int nr; 	list = filelist(t, cp->text->r, cp->text->n);	if(list == nil)		editerror(Enoname);	r = list;	nr = runestrlen(r);	r = skipbl(r, nr, &nr);	if(nr == 0)		new(t, t, nil, 0, 0, r, 0);	else while(nr > 0){		s = findbl(r, nr, &nr);		*s = '\0';		new(t, t, nil, 0, 0, r, runestrlen(r));		if(nr > 0)			r = skipbl(s+1, nr-1, &nr);	}	clearcollection();	return TRUE;} intc_cmd(Text *t, Cmd *cp){	elogreplace(t->file, addr.r.q0, addr.r.q1, cp->text->r, cp->text->n);	t->q0 = addr.r.q0;	t->q1 = addr.r.q0;	return TRUE;} intd_cmd(Text *t, Cmd*){	if(addr.r.q1 > addr.r.q0)		elogdelete(t->file, addr.r.q0, addr.r.q1);	t->q0 = addr.r.q0;	t->q1 = addr.r.q0;	return TRUE;} voidD1(Text *t){	if(t->w->body.file->ntext>1 || winclean(t->w, FALSE))		colclose(t->col, t->w, TRUE);} intD_cmd(Text *t, Cmd *cp){	Rune *list, *r, *s, *n;	int nr, nn;	Window *w;	Runestr dir, rs;	char buf[128]; 	list = filelist(t, cp->text->r, cp->text->n);	if(list == nil){		D1(t);		return TRUE;	}	dir = dirname(t, nil, 0);	r = list;	nr = runestrlen(r);	r = skipbl(r, nr, &nr);	do{		s = findbl(r, nr, &nr);		*s = '\0';		/* first time through, could be empty string, meaning delete file empty name */		nn = runestrlen(r);		if(r[0]=='/' || nn==0 || dir.nr==0){			rs.r = runestrdup(r);			rs.nr = nn;		}else{			n = runemalloc(dir.nr+1+nn);			runemove(n, dir.r, dir.nr);			n[dir.nr] = '/';			runemove(n+dir.nr+1, r, nn);			rs = cleanrname((Runestr){n, dir.nr+1+nn});		}		w = lookfile(rs.r, rs.nr);		if(w == nil){			snprint(buf, sizeof buf, "no such file %.*S", rs.nr, rs.r);			free(rs.r);			editerror(buf);		}		free(rs.r);		D1(&w->body);		if(nr > 0)			r = skipbl(s+1, nr-1, &nr);	}while(nr > 0);	clearcollection();	free(dir.r);	return TRUE;} static intreadloader(void *v, uint q0, Rune *r, int nr){	if(nr > 0)		eloginsert(v, q0, r, nr);	return 0;} inte_cmd(Text *t, Cmd *cp){	Rune *name;	File *f;	int i, isdir, q0, q1, fd, nulls, samename, allreplaced;	char *s, tmp[128];	Dir *d; 	f = t->file;	q0 = addr.r.q0;	q1 = addr.r.q1;	if(cp->cmdc == 'e'){		if(winclean(t->w, TRUE)==FALSE)			editerror("");	/* winclean generated message already */		q0 = 0;		q1 = f->nc;	}	allreplaced = (q0==0 && q1==f->nc);	name = cmdname(f, cp->text, cp->cmdc=='e');	if(name == nil)		editerror(Enoname);	i = runestrlen(name);	samename = runeeq(name, i, t->file->name, t->file->nname);	s = runetobyte(name, i);	free(name);	fd = open(s, OREAD);	if(fd < 0){		snprint(tmp, sizeof tmp, "can't open %s: %r", s);		free(s);		editerror(tmp);	}	d = dirfstat(fd);	isdir = (d!=nil && (d->qid.type&QTDIR));	free(d);	if(isdir){		close(fd);		snprint(tmp, sizeof tmp, "%s is a directory", s);		free(s);		editerror(tmp);	}	elogdelete(f, q0, q1);	nulls = 0;	loadfile(fd, q1, &nulls, readloader, f);	free(s);	close(fd);	if(nulls)		warning(nil, "%s: NUL bytes elided\n", s);	else if(allreplaced && samename)		f->editclean = TRUE;	return TRUE;} intf_cmd(Text *t, Cmd *cp){	Rune *name;	String *str;	String empty; 	if(cp->text == nil){		empty.n = 0;		empty.r = L"";		str = &empty;	}else		str = cp->text;	name = cmdname(t->file, str, TRUE);	free(name);	pfilename(t->file);	return TRUE;} intg_cmd(Text *t, Cmd *cp){	if(t->file != addr.f){		warning(nil, "internal error: g_cmd f!=addr.f\n");		return FALSE;	}	if(rxcompile(cp->re->r) == FALSE)		editerror("bad regexp in g command");	if(rxexecute(t, nil, addr.r.q0, addr.r.q1, &sel) ^ cp->cmdc=='v'){		t->q0 = addr.r.q0;		t->q1 = addr.r.q1;		return cmdexec(t, cp->cmd);	}	return TRUE;} inti_cmd(Text *t, Cmd *cp){	return append(t->file, cp, addr.r.q0);} voidcopy(File *f, Address addr2){	long p;	int ni;	Rune *buf; 	buf = fbufalloc();	for(p=addr.r.q0; p<addr.r.q1; p+=ni){		ni = addr.r.q1-p;		if(ni > RBUFSIZE)			ni = RBUFSIZE;		bufread(f, p, buf, ni);		eloginsert(addr2.f, addr2.r.q1, buf, ni);	}	fbuffree(buf);} voidmove(File *f, Address addr2){	if(addr.f!=addr2.f || addr.r.q1<=addr2.r.q0){		elogdelete(f, addr.r.q0, addr.r.q1);		copy(f, addr2);	}else if(addr.r.q0 >= addr2.r.q1){		copy(f, addr2);		elogdelete(f, addr.r.q0, addr.r.q1);	}else if(addr.r.q0==addr2.r.q0 && addr.r.q1==addr2.r.q1){		;	/* move to self; no-op */	}else		editerror("move overlaps itself");} intm_cmd(Text *t, Cmd *cp){	Address dot, addr2; 	mkaddr(&dot, t->file);	addr2 = cmdaddress(cp->mtaddr, dot, 0);	if(cp->cmdc == 'm')		move(t->file, addr2);	else		copy(t->file, addr2);	return TRUE;} intp_cmd(Text *t, Cmd*){	return pdisplay(t->file);} ints_cmd(Text *t, Cmd *cp){	int i, j, k, c, m, n, nrp, didsub;	long p1, op, delta;	String *buf;	Rangeset *rp;	char *err;	Rune *rbuf; 	n = cp->num;	op= -1;	if(rxcompile(cp->re->r) == FALSE)		editerror("bad regexp in s command");	nrp = 0;	rp = nil;	delta = 0;	didsub = FALSE;	for(p1 = addr.r.q0; p1<=addr.r.q1 && rxexecute(t, nil, p1, addr.r.q1, &sel); ){		if(sel.r[0].q0 == sel.r[0].q1){	/* empty match? */			if(sel.r[0].q0 == op){				p1++;				continue;			}			p1 = sel.r[0].q1+1;		}else			p1 = sel.r[0].q1;		op = sel.r[0].q1;		if(--n>0)			continue;		nrp++;		rp = erealloc(rp, nrp*sizeof(Rangeset));		rp[nrp-1] = sel;	}	rbuf = fbufalloc();	buf = allocstring(0);	for(m=0; m<nrp; m++){		buf->n = 0;		buf->r[0] = L'\0';		sel = rp[m];		for(i = 0; i<cp->text->n; i++)			if((c = cp->text->r[i])=='\\' && i<cp->text->n-1){				c = cp->text->r[++i];				if('1'<=c && c<='9') {					j = c-'0';					if(sel.r[j].q1-sel.r[j].q0>RBUFSIZE){						err = "replacement string too long";						goto Err;					}					bufread(t->file, sel.r[j].q0, rbuf, sel.r[j].q1-sel.r[j].q0);

Lines 1–500 of 1329.