xref: /plan9/sys/src/cmd/acme/edit.c (revision af2e6ba6a88ebbe37fe767755ab16d53fbb9977b)
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 static char	linex[]="\n";
1659cc4ca5SDavid du Colombier static char	wordx[]=" \t\n";
1759cc4ca5SDavid du Colombier struct cmdtab cmdtab[]={
1859cc4ca5SDavid du Colombier /*	cmdc	text	regexp	addr	defcmd	defaddr	count	token	 fn	*/
1959cc4ca5SDavid du Colombier 	'\n',	0,	0,	0,	0,	aDot,	0,	0,	nl_cmd,
2059cc4ca5SDavid du Colombier 	'a',	1,	0,	0,	0,	aDot,	0,	0,	a_cmd,
2159cc4ca5SDavid du Colombier 	'b',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
2259cc4ca5SDavid du Colombier 	'c',	1,	0,	0,	0,	aDot,	0,	0,	c_cmd,
2359cc4ca5SDavid du Colombier 	'd',	0,	0,	0,	0,	aDot,	0,	0,	d_cmd,
2459cc4ca5SDavid du Colombier 	'e',	0,	0,	0,	0,	aNo,	0,	wordx,	e_cmd,
2559cc4ca5SDavid du Colombier 	'f',	0,	0,	0,	0,	aNo,	0,	wordx,	f_cmd,
2659cc4ca5SDavid du Colombier 	'g',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
2759cc4ca5SDavid du Colombier 	'i',	1,	0,	0,	0,	aDot,	0,	0,	i_cmd,
2859cc4ca5SDavid du Colombier 	'm',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
2959cc4ca5SDavid du Colombier 	'p',	0,	0,	0,	0,	aDot,	0,	0,	p_cmd,
3059cc4ca5SDavid du Colombier 	'r',	0,	0,	0,	0,	aDot,	0,	wordx,	e_cmd,
3159cc4ca5SDavid du Colombier 	's',	0,	1,	0,	0,	aDot,	1,	0,	s_cmd,
3259cc4ca5SDavid du Colombier 	't',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
3359cc4ca5SDavid du Colombier 	'u',	0,	0,	0,	0,	aNo,	2,	0,	u_cmd,
3459cc4ca5SDavid du Colombier 	'v',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
3559cc4ca5SDavid du Colombier 	'w',	0,	0,	0,	0,	aAll,	0,	wordx,	w_cmd,
3659cc4ca5SDavid du Colombier 	'x',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
3759cc4ca5SDavid du Colombier 	'y',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
3859cc4ca5SDavid du Colombier 	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
3959cc4ca5SDavid du Colombier 	'B',	0,	0,	0,	0,	aNo,	0,	linex,	B_cmd,
4059cc4ca5SDavid du Colombier 	'D',	0,	0,	0,	0,	aNo,	0,	linex,	D_cmd,
4159cc4ca5SDavid du Colombier 	'X',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
4259cc4ca5SDavid du Colombier 	'Y',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
4359cc4ca5SDavid du Colombier 	'<',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
4459cc4ca5SDavid du Colombier 	'|',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
4559cc4ca5SDavid du Colombier 	'>',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
469a747e4fSDavid du Colombier /* deliberately unimplemented:
4759cc4ca5SDavid du Colombier 	'k',	0,	0,	0,	0,	aDot,	0,	0,	k_cmd,
4859cc4ca5SDavid du Colombier 	'n',	0,	0,	0,	0,	aNo,	0,	0,	n_cmd,
4959cc4ca5SDavid du Colombier 	'q',	0,	0,	0,	0,	aNo,	0,	0,	q_cmd,
5059cc4ca5SDavid du Colombier 	'!',	0,	0,	0,	0,	aNo,	0,	linex,	plan9_cmd,
519a747e4fSDavid du Colombier  */
5259cc4ca5SDavid du Colombier 	0,	0,	0,	0,	0,	0,	0,	0,
5359cc4ca5SDavid du Colombier };
5459cc4ca5SDavid du Colombier 
5559cc4ca5SDavid du Colombier Cmd	*parsecmd(int);
5659cc4ca5SDavid du Colombier Addr	*compoundaddr(void);
5759cc4ca5SDavid du Colombier Addr	*simpleaddr(void);
5859cc4ca5SDavid du Colombier void	freecmd(void);
5959cc4ca5SDavid du Colombier void	okdelim(int);
6059cc4ca5SDavid du Colombier 
6159cc4ca5SDavid du Colombier Rune	*cmdstartp;
6259cc4ca5SDavid du Colombier Rune	*cmdendp;
6359cc4ca5SDavid du Colombier Rune	*cmdp;
6459cc4ca5SDavid du Colombier Channel	*editerrc;
6559cc4ca5SDavid du Colombier 
6659cc4ca5SDavid du Colombier String	*lastpat;
6759cc4ca5SDavid du Colombier int	patset;
6859cc4ca5SDavid du Colombier 
6959cc4ca5SDavid du Colombier List	cmdlist;
7059cc4ca5SDavid du Colombier List	addrlist;
7159cc4ca5SDavid du Colombier List	stringlist;
7259cc4ca5SDavid du Colombier Text	*curtext;
7359cc4ca5SDavid du Colombier int	editing = Inactive;
7459cc4ca5SDavid du Colombier 
7559cc4ca5SDavid du Colombier String*	newstring(int);
7659cc4ca5SDavid du Colombier 
7759cc4ca5SDavid du Colombier void
editthread(void *)7859cc4ca5SDavid du Colombier editthread(void*)
7959cc4ca5SDavid du Colombier {
8059cc4ca5SDavid du Colombier 	Cmd *cmdp;
8159cc4ca5SDavid du Colombier 
8259cc4ca5SDavid du Colombier 	threadsetname("editthread");
8359cc4ca5SDavid du Colombier 	while((cmdp=parsecmd(0)) != 0){
8459cc4ca5SDavid du Colombier //		ocurfile = curfile;
8559cc4ca5SDavid du Colombier //		loaded = curfile && !curfile->unread;
8659cc4ca5SDavid du Colombier 		if(cmdexec(curtext, cmdp) == 0)
8759cc4ca5SDavid du Colombier 			break;
8859cc4ca5SDavid du Colombier 		freecmd();
8959cc4ca5SDavid du Colombier 	}
9059cc4ca5SDavid du Colombier 	sendp(editerrc, nil);
9159cc4ca5SDavid du Colombier }
9259cc4ca5SDavid du Colombier 
9359cc4ca5SDavid du Colombier void
allelogterm(Window * w,void *)9459cc4ca5SDavid du Colombier allelogterm(Window *w, void*)
9559cc4ca5SDavid du Colombier {
9659cc4ca5SDavid du Colombier 	elogterm(w->body.file);
9759cc4ca5SDavid du Colombier }
9859cc4ca5SDavid du Colombier 
9959cc4ca5SDavid du Colombier void
alleditinit(Window * w,void *)10059cc4ca5SDavid du Colombier alleditinit(Window *w, void*)
10159cc4ca5SDavid du Colombier {
10259cc4ca5SDavid du Colombier 	textcommit(&w->tag, TRUE);
10359cc4ca5SDavid du Colombier 	textcommit(&w->body, TRUE);
10459cc4ca5SDavid du Colombier 	w->body.file->editclean = FALSE;
10559cc4ca5SDavid du Colombier }
10659cc4ca5SDavid du Colombier 
10759cc4ca5SDavid du Colombier void
allupdate(Window * w,void *)10859cc4ca5SDavid du Colombier allupdate(Window *w, void*)
10959cc4ca5SDavid du Colombier {
11059cc4ca5SDavid du Colombier 	Text *t;
11159cc4ca5SDavid du Colombier 	int i;
11259cc4ca5SDavid du Colombier 	File *f;
11359cc4ca5SDavid du Colombier 
11459cc4ca5SDavid du Colombier 	t = &w->body;
11559cc4ca5SDavid du Colombier 	f = t->file;
11659cc4ca5SDavid du Colombier 	if(f->curtext != t)	/* do curtext only */
11759cc4ca5SDavid du Colombier 		return;
11859cc4ca5SDavid du Colombier 	if(f->elog.type == Null)
11959cc4ca5SDavid du Colombier 		elogterm(f);
12059cc4ca5SDavid du Colombier 	else if(f->elog.type != Empty){
12159cc4ca5SDavid du Colombier 		elogapply(f);
12259cc4ca5SDavid du Colombier 		if(f->editclean){
12359cc4ca5SDavid du Colombier 			f->mod = FALSE;
12459cc4ca5SDavid du Colombier 			for(i=0; i<f->ntext; i++)
12559cc4ca5SDavid du Colombier 				f->text[i]->w->dirty = FALSE;
12659cc4ca5SDavid du Colombier 		}
12780ee5cbfSDavid du Colombier 	}
12859cc4ca5SDavid du Colombier 	textsetselect(t, t->q0, t->q1);
12959cc4ca5SDavid du Colombier 	textscrdraw(t);
13059cc4ca5SDavid du Colombier 	winsettag(w);
13159cc4ca5SDavid du Colombier }
13259cc4ca5SDavid du Colombier 
13359cc4ca5SDavid du Colombier void
editerror(char * fmt,...)13459cc4ca5SDavid du Colombier editerror(char *fmt, ...)
13559cc4ca5SDavid du Colombier {
13659cc4ca5SDavid du Colombier 	va_list arg;
13759cc4ca5SDavid du Colombier 	char *s;
13859cc4ca5SDavid du Colombier 
13959cc4ca5SDavid du Colombier 	va_start(arg, fmt);
1409a747e4fSDavid du Colombier 	s = vsmprint(fmt, arg);
14159cc4ca5SDavid du Colombier 	va_end(arg);
14259cc4ca5SDavid du Colombier 	freecmd();
14359cc4ca5SDavid du Colombier 	allwindows(allelogterm, nil);	/* truncate the edit logs */
14459cc4ca5SDavid du Colombier 	sendp(editerrc, s);
14559cc4ca5SDavid du Colombier 	threadexits(nil);
14659cc4ca5SDavid du Colombier }
14759cc4ca5SDavid du Colombier 
14859cc4ca5SDavid du Colombier void
editcmd(Text * ct,Rune * r,uint n)14959cc4ca5SDavid du Colombier editcmd(Text *ct, Rune *r, uint n)
15059cc4ca5SDavid du Colombier {
15159cc4ca5SDavid du Colombier 	char *err;
15259cc4ca5SDavid du Colombier 
15359cc4ca5SDavid du Colombier 	if(n == 0)
15459cc4ca5SDavid du Colombier 		return;
15559cc4ca5SDavid du Colombier 	if(2*n > RBUFSIZE){
15659cc4ca5SDavid du Colombier 		warning(nil, "string too long\n");
15759cc4ca5SDavid du Colombier 		return;
15859cc4ca5SDavid du Colombier 	}
15959cc4ca5SDavid du Colombier 
16059cc4ca5SDavid du Colombier 	allwindows(alleditinit, nil);
16159cc4ca5SDavid du Colombier 	if(cmdstartp)
16259cc4ca5SDavid du Colombier 		free(cmdstartp);
16359cc4ca5SDavid du Colombier 	cmdstartp = runemalloc(n+2);
16459cc4ca5SDavid du Colombier 	runemove(cmdstartp, r, n);
16559cc4ca5SDavid du Colombier 	if(r[n] != '\n')
16659cc4ca5SDavid du Colombier 		cmdstartp[n++] = '\n';
16759cc4ca5SDavid du Colombier 	cmdstartp[n] = '\0';
16859cc4ca5SDavid du Colombier 	cmdendp = cmdstartp+n;
16959cc4ca5SDavid du Colombier 	cmdp = cmdstartp;
17059cc4ca5SDavid du Colombier 	if(ct->w == nil)
17159cc4ca5SDavid du Colombier 		curtext = nil;
17259cc4ca5SDavid du Colombier 	else
17359cc4ca5SDavid du Colombier 		curtext = &ct->w->body;
17459cc4ca5SDavid du Colombier 	resetxec();
17559cc4ca5SDavid du Colombier 	if(editerrc == nil){
17659cc4ca5SDavid du Colombier 		editerrc = chancreate(sizeof(char*), 0);
17759cc4ca5SDavid du Colombier 		lastpat = allocstring(0);
17859cc4ca5SDavid du Colombier 	}
17959cc4ca5SDavid du Colombier 	threadcreate(editthread, nil, STACK);
18059cc4ca5SDavid du Colombier 	err = recvp(editerrc);
18159cc4ca5SDavid du Colombier 	editing = Inactive;
18259cc4ca5SDavid du Colombier 	if(err != nil){
18359cc4ca5SDavid du Colombier 		if(err[0] != '\0')
18459cc4ca5SDavid du Colombier 			warning(nil, "Edit: %s\n", err);
18559cc4ca5SDavid du Colombier 		free(err);
18659cc4ca5SDavid du Colombier 	}
18759cc4ca5SDavid du Colombier 
18859cc4ca5SDavid du Colombier 	/* update everyone whose edit log has data */
18959cc4ca5SDavid du Colombier 	allwindows(allupdate, nil);
19059cc4ca5SDavid du Colombier }
19159cc4ca5SDavid du Colombier 
19259cc4ca5SDavid du Colombier int
getch(void)19359cc4ca5SDavid du Colombier getch(void)
19459cc4ca5SDavid du Colombier {
19559cc4ca5SDavid du Colombier 	if(*cmdp == *cmdendp)
19659cc4ca5SDavid du Colombier 		return -1;
19759cc4ca5SDavid du Colombier 	return *cmdp++;
19859cc4ca5SDavid du Colombier }
19959cc4ca5SDavid du Colombier 
20059cc4ca5SDavid du Colombier int
nextc(void)20159cc4ca5SDavid du Colombier nextc(void)
20259cc4ca5SDavid du Colombier {
20359cc4ca5SDavid du Colombier 	if(*cmdp == *cmdendp)
20459cc4ca5SDavid du Colombier 		return -1;
20559cc4ca5SDavid du Colombier 	return *cmdp;
20659cc4ca5SDavid du Colombier }
20759cc4ca5SDavid du Colombier 
20859cc4ca5SDavid du Colombier void
ungetch(void)20959cc4ca5SDavid du Colombier ungetch(void)
21059cc4ca5SDavid du Colombier {
21159cc4ca5SDavid du Colombier 	if(--cmdp < cmdstartp)
21259cc4ca5SDavid du Colombier 		error("ungetch");
21359cc4ca5SDavid du Colombier }
21459cc4ca5SDavid du Colombier 
21559cc4ca5SDavid du Colombier long
getnum(int signok)21659cc4ca5SDavid du Colombier getnum(int signok)
21759cc4ca5SDavid du Colombier {
21859cc4ca5SDavid du Colombier 	long n;
21959cc4ca5SDavid du Colombier 	int c, sign;
22059cc4ca5SDavid du Colombier 
22159cc4ca5SDavid du Colombier 	n = 0;
22259cc4ca5SDavid du Colombier 	sign = 1;
22359cc4ca5SDavid du Colombier 	if(signok>1 && nextc()=='-'){
22459cc4ca5SDavid du Colombier 		sign = -1;
22559cc4ca5SDavid du Colombier 		getch();
22659cc4ca5SDavid du Colombier 	}
22759cc4ca5SDavid du Colombier 	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
22859cc4ca5SDavid du Colombier 		return sign;
22959cc4ca5SDavid du Colombier 	while('0'<=(c=getch()) && c<='9')
23059cc4ca5SDavid du Colombier 		n = n*10 + (c-'0');
23159cc4ca5SDavid du Colombier 	ungetch();
23259cc4ca5SDavid du Colombier 	return sign*n;
23359cc4ca5SDavid du Colombier }
23459cc4ca5SDavid du Colombier 
23559cc4ca5SDavid du Colombier int
cmdskipbl(void)23659cc4ca5SDavid du Colombier cmdskipbl(void)
23759cc4ca5SDavid du Colombier {
23859cc4ca5SDavid du Colombier 	int c;
23959cc4ca5SDavid du Colombier 	do
24059cc4ca5SDavid du Colombier 		c = getch();
24159cc4ca5SDavid du Colombier 	while(c==' ' || c=='\t');
24259cc4ca5SDavid du Colombier 	if(c >= 0)
24359cc4ca5SDavid du Colombier 		ungetch();
24459cc4ca5SDavid du Colombier 	return c;
24559cc4ca5SDavid du Colombier }
24659cc4ca5SDavid du Colombier 
24759cc4ca5SDavid du Colombier /*
24859cc4ca5SDavid du Colombier  * Check that list has room for one more element.
24959cc4ca5SDavid du Colombier  */
25059cc4ca5SDavid du Colombier void
growlist(List * l)25159cc4ca5SDavid du Colombier growlist(List *l)
25259cc4ca5SDavid du Colombier {
25359cc4ca5SDavid du Colombier 	if(l->listptr==0 || l->nalloc==0){
25459cc4ca5SDavid du Colombier 		l->nalloc = INCR;
255*af2e6ba6SDavid du Colombier 		l->listptr = emalloc(INCR*sizeof(void*));
25659cc4ca5SDavid du Colombier 		l->nused = 0;
25759cc4ca5SDavid du Colombier 	}else if(l->nused == l->nalloc){
258*af2e6ba6SDavid du Colombier 		l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*));
259*af2e6ba6SDavid du Colombier 		memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*));
26059cc4ca5SDavid du Colombier 		l->nalloc += INCR;
26159cc4ca5SDavid du Colombier 	}
26259cc4ca5SDavid du Colombier }
26359cc4ca5SDavid du Colombier 
26459cc4ca5SDavid du Colombier /*
26559cc4ca5SDavid du Colombier  * Remove the ith element from the list
26659cc4ca5SDavid du Colombier  */
26759cc4ca5SDavid du Colombier void
dellist(List * l,int i)26859cc4ca5SDavid du Colombier dellist(List *l, int i)
26959cc4ca5SDavid du Colombier {
270*af2e6ba6SDavid du Colombier 	memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-(i+1))*sizeof(void*));
27159cc4ca5SDavid du Colombier 	l->nused--;
27259cc4ca5SDavid du Colombier }
27359cc4ca5SDavid du Colombier 
27459cc4ca5SDavid du Colombier /*
27559cc4ca5SDavid du Colombier  * Add a new element, whose position is i, to the list
27659cc4ca5SDavid du Colombier  */
27759cc4ca5SDavid du Colombier void
inslist(List * l,int i,void * v)278*af2e6ba6SDavid du Colombier inslist(List *l, int i, void *v)
27959cc4ca5SDavid du Colombier {
28059cc4ca5SDavid du Colombier 	growlist(l);
281*af2e6ba6SDavid du Colombier 	memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*));
282*af2e6ba6SDavid du Colombier 	l->ptr[i] = v;
28359cc4ca5SDavid du Colombier 	l->nused++;
28459cc4ca5SDavid du Colombier }
28559cc4ca5SDavid du Colombier 
28659cc4ca5SDavid du Colombier void
listfree(List * l)28759cc4ca5SDavid du Colombier listfree(List *l)
28859cc4ca5SDavid du Colombier {
28959cc4ca5SDavid du Colombier 	free(l->listptr);
29059cc4ca5SDavid du Colombier 	free(l);
29159cc4ca5SDavid du Colombier }
29259cc4ca5SDavid du Colombier 
29359cc4ca5SDavid du Colombier String*
allocstring(int n)29459cc4ca5SDavid du Colombier allocstring(int n)
29559cc4ca5SDavid du Colombier {
29659cc4ca5SDavid du Colombier 	String *s;
29759cc4ca5SDavid du Colombier 
29859cc4ca5SDavid du Colombier 	s = emalloc(sizeof(String));
29959cc4ca5SDavid du Colombier 	s->n = n;
30059cc4ca5SDavid du Colombier 	s->nalloc = n+10;
30159cc4ca5SDavid du Colombier 	s->r = emalloc(s->nalloc*sizeof(Rune));
30259cc4ca5SDavid du Colombier 	s->r[n] = '\0';
30359cc4ca5SDavid du Colombier 	return s;
30459cc4ca5SDavid du Colombier }
30559cc4ca5SDavid du Colombier 
30659cc4ca5SDavid du Colombier void
freestring(String * s)30759cc4ca5SDavid du Colombier freestring(String *s)
30859cc4ca5SDavid du Colombier {
30959cc4ca5SDavid du Colombier 	free(s->r);
31059cc4ca5SDavid du Colombier 	free(s);
31159cc4ca5SDavid du Colombier }
31259cc4ca5SDavid du Colombier 
31359cc4ca5SDavid du Colombier Cmd*
newcmd(void)31459cc4ca5SDavid du Colombier newcmd(void){
31559cc4ca5SDavid du Colombier 	Cmd *p;
31659cc4ca5SDavid du Colombier 
31759cc4ca5SDavid du Colombier 	p = emalloc(sizeof(Cmd));
318*af2e6ba6SDavid du Colombier 	inslist(&cmdlist, cmdlist.nused, p);
31959cc4ca5SDavid du Colombier 	return p;
32059cc4ca5SDavid du Colombier }
32159cc4ca5SDavid du Colombier 
32259cc4ca5SDavid du Colombier String*
newstring(int n)32359cc4ca5SDavid du Colombier newstring(int n)
32459cc4ca5SDavid du Colombier {
32559cc4ca5SDavid du Colombier 	String *p;
32659cc4ca5SDavid du Colombier 
32759cc4ca5SDavid du Colombier 	p = allocstring(n);
328*af2e6ba6SDavid du Colombier 	inslist(&stringlist, stringlist.nused, p);
32959cc4ca5SDavid du Colombier 	return p;
33059cc4ca5SDavid du Colombier }
33159cc4ca5SDavid du Colombier 
33259cc4ca5SDavid du Colombier Addr*
newaddr(void)33359cc4ca5SDavid du Colombier newaddr(void)
33459cc4ca5SDavid du Colombier {
33559cc4ca5SDavid du Colombier 	Addr *p;
33659cc4ca5SDavid du Colombier 
33759cc4ca5SDavid du Colombier 	p = emalloc(sizeof(Addr));
338*af2e6ba6SDavid du Colombier 	inslist(&addrlist, addrlist.nused, p);
33959cc4ca5SDavid du Colombier 	return p;
34059cc4ca5SDavid du Colombier }
34159cc4ca5SDavid du Colombier 
34259cc4ca5SDavid du Colombier void
freecmd(void)34359cc4ca5SDavid du Colombier freecmd(void)
34459cc4ca5SDavid du Colombier {
34559cc4ca5SDavid du Colombier 	int i;
34659cc4ca5SDavid du Colombier 
34759cc4ca5SDavid du Colombier 	while(cmdlist.nused > 0)
34859cc4ca5SDavid du Colombier 		free(cmdlist.ucharptr[--cmdlist.nused]);
34959cc4ca5SDavid du Colombier 	while(addrlist.nused > 0)
35059cc4ca5SDavid du Colombier 		free(addrlist.ucharptr[--addrlist.nused]);
35159cc4ca5SDavid du Colombier 	while(stringlist.nused>0){
35259cc4ca5SDavid du Colombier 		i = --stringlist.nused;
35359cc4ca5SDavid du Colombier 		freestring(stringlist.stringptr[i]);
35459cc4ca5SDavid du Colombier 	}
35559cc4ca5SDavid du Colombier }
35659cc4ca5SDavid du Colombier 
35759cc4ca5SDavid du Colombier void
okdelim(int c)35859cc4ca5SDavid du Colombier okdelim(int c)
35959cc4ca5SDavid du Colombier {
36059cc4ca5SDavid du Colombier 	if(c=='\\' || ('a'<=c && c<='z')
36159cc4ca5SDavid du Colombier 	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
36259cc4ca5SDavid du Colombier 		editerror("bad delimiter %c\n", c);
36359cc4ca5SDavid du Colombier }
36459cc4ca5SDavid du Colombier 
36559cc4ca5SDavid du Colombier void
atnl(void)36659cc4ca5SDavid du Colombier atnl(void)
36759cc4ca5SDavid du Colombier {
36859cc4ca5SDavid du Colombier 	int c;
36959cc4ca5SDavid du Colombier 
37059cc4ca5SDavid du Colombier 	cmdskipbl();
37159cc4ca5SDavid du Colombier 	c = getch();
37259cc4ca5SDavid du Colombier 	if(c != '\n')
37359cc4ca5SDavid du Colombier 		editerror("newline expected (saw %C)", c);
37459cc4ca5SDavid du Colombier }
37559cc4ca5SDavid du Colombier 
37659cc4ca5SDavid du Colombier void
Straddc(String * s,int c)37759cc4ca5SDavid du Colombier Straddc(String *s, int c)
37859cc4ca5SDavid du Colombier {
37959cc4ca5SDavid du Colombier 	if(s->n+1 >= s->nalloc){
38059cc4ca5SDavid du Colombier 		s->nalloc += 10;
38159cc4ca5SDavid du Colombier 		s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
38259cc4ca5SDavid du Colombier 	}
38359cc4ca5SDavid du Colombier 	s->r[s->n++] = c;
38459cc4ca5SDavid du Colombier 	s->r[s->n] = '\0';
38559cc4ca5SDavid du Colombier }
38659cc4ca5SDavid du Colombier 
38759cc4ca5SDavid du Colombier void
getrhs(String * s,int delim,int cmd)38859cc4ca5SDavid du Colombier getrhs(String *s, int delim, int cmd)
38959cc4ca5SDavid du Colombier {
39059cc4ca5SDavid du Colombier 	int c;
39159cc4ca5SDavid du Colombier 
39259cc4ca5SDavid du Colombier 	while((c = getch())>0 && c!=delim && c!='\n'){
39359cc4ca5SDavid du Colombier 		if(c == '\\'){
39459cc4ca5SDavid du Colombier 			if((c=getch()) <= 0)
39559cc4ca5SDavid du Colombier 				error("bad right hand side");
39659cc4ca5SDavid du Colombier 			if(c == '\n'){
39759cc4ca5SDavid du Colombier 				ungetch();
39859cc4ca5SDavid du Colombier 				c='\\';
39959cc4ca5SDavid du Colombier 			}else if(c == 'n')
40059cc4ca5SDavid du Colombier 				c='\n';
40159cc4ca5SDavid du Colombier 			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
40259cc4ca5SDavid du Colombier 				Straddc(s, '\\');
40359cc4ca5SDavid du Colombier 		}
40459cc4ca5SDavid du Colombier 		Straddc(s, c);
40559cc4ca5SDavid du Colombier 	}
40659cc4ca5SDavid du Colombier 	ungetch();	/* let client read whether delimiter, '\n' or whatever */
40759cc4ca5SDavid du Colombier }
40859cc4ca5SDavid du Colombier 
40959cc4ca5SDavid du Colombier String *
collecttoken(char * end)41059cc4ca5SDavid du Colombier collecttoken(char *end)
41159cc4ca5SDavid du Colombier {
41259cc4ca5SDavid du Colombier 	String *s = newstring(0);
41359cc4ca5SDavid du Colombier 	int c;
41459cc4ca5SDavid du Colombier 
41559cc4ca5SDavid du Colombier 	while((c=nextc())==' ' || c=='\t')
41659cc4ca5SDavid du Colombier 		Straddc(s, getch()); /* blanks significant for getname() */
41759cc4ca5SDavid du Colombier 	while((c=getch())>0 && utfrune(end, c)==0)
41859cc4ca5SDavid du Colombier 		Straddc(s, c);
41959cc4ca5SDavid du Colombier 	if(c != '\n')
42059cc4ca5SDavid du Colombier 		atnl();
42159cc4ca5SDavid du Colombier 	return s;
42259cc4ca5SDavid du Colombier }
42359cc4ca5SDavid du Colombier 
42459cc4ca5SDavid du Colombier String *
collecttext(void)42559cc4ca5SDavid du Colombier collecttext(void)
42659cc4ca5SDavid du Colombier {
42759cc4ca5SDavid du Colombier 	String *s;
42859cc4ca5SDavid du Colombier 	int begline, i, c, delim;
42959cc4ca5SDavid du Colombier 
43059cc4ca5SDavid du Colombier 	s = newstring(0);
43159cc4ca5SDavid du Colombier 	if(cmdskipbl()=='\n'){
43259cc4ca5SDavid du Colombier 		getch();
43359cc4ca5SDavid du Colombier 		i = 0;
43459cc4ca5SDavid du Colombier 		do{
43559cc4ca5SDavid du Colombier 			begline = i;
43659cc4ca5SDavid du Colombier 			while((c = getch())>0 && c!='\n')
43759cc4ca5SDavid du Colombier 				i++, Straddc(s, c);
43859cc4ca5SDavid du Colombier 			i++, Straddc(s, '\n');
43959cc4ca5SDavid du Colombier 			if(c < 0)
44059cc4ca5SDavid du Colombier 				goto Return;
44159cc4ca5SDavid du Colombier 		}while(s->r[begline]!='.' || s->r[begline+1]!='\n');
44259cc4ca5SDavid du Colombier 		s->r[s->n-2] = '\0';
4434b30ca09SDavid du Colombier 		s->n -= 2;
44459cc4ca5SDavid du Colombier 	}else{
44559cc4ca5SDavid du Colombier 		okdelim(delim = getch());
44659cc4ca5SDavid du Colombier 		getrhs(s, delim, 'a');
44759cc4ca5SDavid du Colombier 		if(nextc()==delim)
44859cc4ca5SDavid du Colombier 			getch();
44959cc4ca5SDavid du Colombier 		atnl();
45059cc4ca5SDavid du Colombier 	}
45159cc4ca5SDavid du Colombier     Return:
45259cc4ca5SDavid du Colombier 	return s;
45359cc4ca5SDavid du Colombier }
45459cc4ca5SDavid du Colombier 
45559cc4ca5SDavid du Colombier int
cmdlookup(int c)45659cc4ca5SDavid du Colombier cmdlookup(int c)
45759cc4ca5SDavid du Colombier {
45859cc4ca5SDavid du Colombier 	int i;
45959cc4ca5SDavid du Colombier 
46059cc4ca5SDavid du Colombier 	for(i=0; cmdtab[i].cmdc; i++)
46159cc4ca5SDavid du Colombier 		if(cmdtab[i].cmdc == c)
46259cc4ca5SDavid du Colombier 			return i;
46359cc4ca5SDavid du Colombier 	return -1;
46459cc4ca5SDavid du Colombier }
46559cc4ca5SDavid du Colombier 
46659cc4ca5SDavid du Colombier Cmd*
parsecmd(int nest)46759cc4ca5SDavid du Colombier parsecmd(int nest)
46859cc4ca5SDavid du Colombier {
46959cc4ca5SDavid du Colombier 	int i, c;
47059cc4ca5SDavid du Colombier 	struct cmdtab *ct;
47159cc4ca5SDavid du Colombier 	Cmd *cp, *ncp;
47259cc4ca5SDavid du Colombier 	Cmd cmd;
47359cc4ca5SDavid du Colombier 
47459cc4ca5SDavid du Colombier 	cmd.next = cmd.cmd = 0;
47559cc4ca5SDavid du Colombier 	cmd.re = 0;
47659cc4ca5SDavid du Colombier 	cmd.flag = cmd.num = 0;
47759cc4ca5SDavid du Colombier 	cmd.addr = compoundaddr();
47859cc4ca5SDavid du Colombier 	if(cmdskipbl() == -1)
47959cc4ca5SDavid du Colombier 		return 0;
48059cc4ca5SDavid du Colombier 	if((c=getch())==-1)
48159cc4ca5SDavid du Colombier 		return 0;
48259cc4ca5SDavid du Colombier 	cmd.cmdc = c;
48359cc4ca5SDavid du Colombier 	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
48459cc4ca5SDavid du Colombier 		getch();		/* the 'd' */
48559cc4ca5SDavid du Colombier 		cmd.cmdc='c'|0x100;
48659cc4ca5SDavid du Colombier 	}
48759cc4ca5SDavid du Colombier 	i = cmdlookup(cmd.cmdc);
48859cc4ca5SDavid du Colombier 	if(i >= 0){
48959cc4ca5SDavid du Colombier 		if(cmd.cmdc == '\n')
49059cc4ca5SDavid du Colombier 			goto Return;	/* let nl_cmd work it all out */
49159cc4ca5SDavid du Colombier 		ct = &cmdtab[i];
49259cc4ca5SDavid du Colombier 		if(ct->defaddr==aNo && cmd.addr)
49359cc4ca5SDavid du Colombier 			editerror("command takes no address");
49459cc4ca5SDavid du Colombier 		if(ct->count)
49559cc4ca5SDavid du Colombier 			cmd.num = getnum(ct->count);
49659cc4ca5SDavid du Colombier 		if(ct->regexp){
49759cc4ca5SDavid du Colombier 			/* x without pattern -> .*\n, indicated by cmd.re==0 */
49859cc4ca5SDavid du Colombier 			/* X without pattern is all files */
49959cc4ca5SDavid du Colombier 			if((ct->cmdc!='x' && ct->cmdc!='X') ||
50059cc4ca5SDavid du Colombier 			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
50159cc4ca5SDavid du Colombier 				cmdskipbl();
50259cc4ca5SDavid du Colombier 				if((c = getch())=='\n' || c<0)
50359cc4ca5SDavid du Colombier 					editerror("no address");
50459cc4ca5SDavid du Colombier 				okdelim(c);
50559cc4ca5SDavid du Colombier 				cmd.re = getregexp(c);
50659cc4ca5SDavid du Colombier 				if(ct->cmdc == 's'){
50759cc4ca5SDavid du Colombier 					cmd.text = newstring(0);
50859cc4ca5SDavid du Colombier 					getrhs(cmd.text, c, 's');
50959cc4ca5SDavid du Colombier 					if(nextc() == c){
51059cc4ca5SDavid du Colombier 						getch();
51159cc4ca5SDavid du Colombier 						if(nextc() == 'g')
51259cc4ca5SDavid du Colombier 							cmd.flag = getch();
51359cc4ca5SDavid du Colombier 					}
51459cc4ca5SDavid du Colombier 
51559cc4ca5SDavid du Colombier 				}
51659cc4ca5SDavid du Colombier 			}
51759cc4ca5SDavid du Colombier 		}
51859cc4ca5SDavid du Colombier 		if(ct->addr && (cmd.mtaddr=simpleaddr())==0)
51959cc4ca5SDavid du Colombier 			editerror("bad address");
52059cc4ca5SDavid du Colombier 		if(ct->defcmd){
52159cc4ca5SDavid du Colombier 			if(cmdskipbl() == '\n'){
52259cc4ca5SDavid du Colombier 				getch();
52359cc4ca5SDavid du Colombier 				cmd.cmd = newcmd();
52459cc4ca5SDavid du Colombier 				cmd.cmd->cmdc = ct->defcmd;
52559cc4ca5SDavid du Colombier 			}else if((cmd.cmd = parsecmd(nest))==0)
52659cc4ca5SDavid du Colombier 				error("defcmd");
52759cc4ca5SDavid du Colombier 		}else if(ct->text)
52859cc4ca5SDavid du Colombier 			cmd.text = collecttext();
52959cc4ca5SDavid du Colombier 		else if(ct->token)
53059cc4ca5SDavid du Colombier 			cmd.text = collecttoken(ct->token);
53159cc4ca5SDavid du Colombier 		else
53259cc4ca5SDavid du Colombier 			atnl();
53359cc4ca5SDavid du Colombier 	}else
53459cc4ca5SDavid du Colombier 		switch(cmd.cmdc){
53559cc4ca5SDavid du Colombier 		case '{':
53659cc4ca5SDavid du Colombier 			cp = 0;
53759cc4ca5SDavid du Colombier 			do{
53859cc4ca5SDavid du Colombier 				if(cmdskipbl()=='\n')
53959cc4ca5SDavid du Colombier 					getch();
54059cc4ca5SDavid du Colombier 				ncp = parsecmd(nest+1);
54159cc4ca5SDavid du Colombier 				if(cp)
54259cc4ca5SDavid du Colombier 					cp->next = ncp;
54359cc4ca5SDavid du Colombier 				else
54459cc4ca5SDavid du Colombier 					cmd.cmd = ncp;
54559cc4ca5SDavid du Colombier 			}while(cp = ncp);
54659cc4ca5SDavid du Colombier 			break;
54759cc4ca5SDavid du Colombier 		case '}':
54859cc4ca5SDavid du Colombier 			atnl();
54959cc4ca5SDavid du Colombier 			if(nest==0)
55059cc4ca5SDavid du Colombier 				editerror("right brace with no left brace");
55159cc4ca5SDavid du Colombier 			return 0;
55259cc4ca5SDavid du Colombier 		default:
55359cc4ca5SDavid du Colombier 			editerror("unknown command %c", cmd.cmdc);
55459cc4ca5SDavid du Colombier 		}
55559cc4ca5SDavid du Colombier     Return:
55659cc4ca5SDavid du Colombier 	cp = newcmd();
55759cc4ca5SDavid du Colombier 	*cp = cmd;
55859cc4ca5SDavid du Colombier 	return cp;
55959cc4ca5SDavid du Colombier }
56059cc4ca5SDavid du Colombier 
56159cc4ca5SDavid du Colombier String*
getregexp(int delim)56259cc4ca5SDavid du Colombier getregexp(int delim)
56359cc4ca5SDavid du Colombier {
56459cc4ca5SDavid du Colombier 	String *buf, *r;
56559cc4ca5SDavid du Colombier 	int i, c;
56659cc4ca5SDavid du Colombier 
56759cc4ca5SDavid du Colombier 	buf = allocstring(0);
56859cc4ca5SDavid du Colombier 	for(i=0; ; i++){
56959cc4ca5SDavid du Colombier 		if((c = getch())=='\\'){
57059cc4ca5SDavid du Colombier 			if(nextc()==delim)
57159cc4ca5SDavid du Colombier 				c = getch();
57259cc4ca5SDavid du Colombier 			else if(nextc()=='\\'){
57359cc4ca5SDavid du Colombier 				Straddc(buf, c);
57459cc4ca5SDavid du Colombier 				c = getch();
57559cc4ca5SDavid du Colombier 			}
57659cc4ca5SDavid du Colombier 		}else if(c==delim || c=='\n')
57759cc4ca5SDavid du Colombier 			break;
57859cc4ca5SDavid du Colombier 		if(i >= RBUFSIZE)
57959cc4ca5SDavid du Colombier 			editerror("regular expression too long");
58059cc4ca5SDavid du Colombier 		Straddc(buf, c);
58159cc4ca5SDavid du Colombier 	}
58259cc4ca5SDavid du Colombier 	if(c!=delim && c)
58359cc4ca5SDavid du Colombier 		ungetch();
58459cc4ca5SDavid du Colombier 	if(buf->n > 0){
58559cc4ca5SDavid du Colombier 		patset = TRUE;
58659cc4ca5SDavid du Colombier 		freestring(lastpat);
58759cc4ca5SDavid du Colombier 		lastpat = buf;
58859cc4ca5SDavid du Colombier 	}else
58959cc4ca5SDavid du Colombier 		freestring(buf);
59059cc4ca5SDavid du Colombier 	if(lastpat->n == 0)
59159cc4ca5SDavid du Colombier 		editerror("no regular expression defined");
59259cc4ca5SDavid du Colombier 	r = newstring(lastpat->n);
59359cc4ca5SDavid du Colombier 	runemove(r->r, lastpat->r, lastpat->n);	/* newstring put \0 at end */
59459cc4ca5SDavid du Colombier 	return r;
59559cc4ca5SDavid du Colombier }
59659cc4ca5SDavid du Colombier 
59759cc4ca5SDavid du Colombier Addr *
simpleaddr(void)59859cc4ca5SDavid du Colombier simpleaddr(void)
59959cc4ca5SDavid du Colombier {
60059cc4ca5SDavid du Colombier 	Addr addr;
60159cc4ca5SDavid du Colombier 	Addr *ap, *nap;
60259cc4ca5SDavid du Colombier 
60359cc4ca5SDavid du Colombier 	addr.next = 0;
60459cc4ca5SDavid du Colombier 	addr.left = 0;
60559cc4ca5SDavid du Colombier 	switch(cmdskipbl()){
60659cc4ca5SDavid du Colombier 	case '#':
60759cc4ca5SDavid du Colombier 		addr.type = getch();
60859cc4ca5SDavid du Colombier 		addr.num = getnum(1);
60959cc4ca5SDavid du Colombier 		break;
61059cc4ca5SDavid du Colombier 	case '0': case '1': case '2': case '3': case '4':
61159cc4ca5SDavid du Colombier 	case '5': case '6': case '7': case '8': case '9':
61259cc4ca5SDavid du Colombier 		addr.num = getnum(1);
61359cc4ca5SDavid du Colombier 		addr.type='l';
61459cc4ca5SDavid du Colombier 		break;
61559cc4ca5SDavid du Colombier 	case '/': case '?': case '"':
61659cc4ca5SDavid du Colombier 		addr.re = getregexp(addr.type = getch());
61759cc4ca5SDavid du Colombier 		break;
61859cc4ca5SDavid du Colombier 	case '.':
61959cc4ca5SDavid du Colombier 	case '$':
62059cc4ca5SDavid du Colombier 	case '+':
62159cc4ca5SDavid du Colombier 	case '-':
62259cc4ca5SDavid du Colombier 	case '\'':
62359cc4ca5SDavid du Colombier 		addr.type = getch();
62459cc4ca5SDavid du Colombier 		break;
62559cc4ca5SDavid du Colombier 	default:
62659cc4ca5SDavid du Colombier 		return 0;
62759cc4ca5SDavid du Colombier 	}
62859cc4ca5SDavid du Colombier 	if(addr.next = simpleaddr())
62959cc4ca5SDavid du Colombier 		switch(addr.next->type){
63059cc4ca5SDavid du Colombier 		case '.':
63159cc4ca5SDavid du Colombier 		case '$':
63259cc4ca5SDavid du Colombier 		case '\'':
63359cc4ca5SDavid du Colombier 			if(addr.type!='"')
63459cc4ca5SDavid du Colombier 		case '"':
63559cc4ca5SDavid du Colombier 				editerror("bad address syntax");
63659cc4ca5SDavid du Colombier 			break;
63759cc4ca5SDavid du Colombier 		case 'l':
63859cc4ca5SDavid du Colombier 		case '#':
63959cc4ca5SDavid du Colombier 			if(addr.type=='"')
64059cc4ca5SDavid du Colombier 				break;
64159cc4ca5SDavid du Colombier 			/* fall through */
64259cc4ca5SDavid du Colombier 		case '/':
64359cc4ca5SDavid du Colombier 		case '?':
64459cc4ca5SDavid du Colombier 			if(addr.type!='+' && addr.type!='-'){
64559cc4ca5SDavid du Colombier 				/* insert the missing '+' */
64659cc4ca5SDavid du Colombier 				nap = newaddr();
64759cc4ca5SDavid du Colombier 				nap->type='+';
64859cc4ca5SDavid du Colombier 				nap->next = addr.next;
64959cc4ca5SDavid du Colombier 				addr.next = nap;
65059cc4ca5SDavid du Colombier 			}
65159cc4ca5SDavid du Colombier 			break;
65259cc4ca5SDavid du Colombier 		case '+':
65359cc4ca5SDavid du Colombier 		case '-':
65459cc4ca5SDavid du Colombier 			break;
65559cc4ca5SDavid du Colombier 		default:
65659cc4ca5SDavid du Colombier 			error("simpleaddr");
65759cc4ca5SDavid du Colombier 		}
65859cc4ca5SDavid du Colombier 	ap = newaddr();
65959cc4ca5SDavid du Colombier 	*ap = addr;
66059cc4ca5SDavid du Colombier 	return ap;
66159cc4ca5SDavid du Colombier }
66259cc4ca5SDavid du Colombier 
66359cc4ca5SDavid du Colombier Addr *
compoundaddr(void)66459cc4ca5SDavid du Colombier compoundaddr(void)
66559cc4ca5SDavid du Colombier {
66659cc4ca5SDavid du Colombier 	Addr addr;
66759cc4ca5SDavid du Colombier 	Addr *ap, *next;
66859cc4ca5SDavid du Colombier 
66959cc4ca5SDavid du Colombier 	addr.left = simpleaddr();
67059cc4ca5SDavid du Colombier 	if((addr.type = cmdskipbl())!=',' && addr.type!=';')
67159cc4ca5SDavid du Colombier 		return addr.left;
67259cc4ca5SDavid du Colombier 	getch();
67359cc4ca5SDavid du Colombier 	next = addr.next = compoundaddr();
67459cc4ca5SDavid du Colombier 	if(next && (next->type==',' || next->type==';') && next->left==0)
67559cc4ca5SDavid du Colombier 		editerror("bad address syntax");
67659cc4ca5SDavid du Colombier 	ap = newaddr();
67759cc4ca5SDavid du Colombier 	*ap = addr;
67859cc4ca5SDavid du Colombier 	return ap;
67959cc4ca5SDavid du Colombier }
680