Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <draw.h>
4
#include <thread.h>
5
#include <cursor.h>
6
#include <mouse.h>
7
#include <keyboard.h>
8
#include <frame.h>
9
#include <fcall.h>
10
#include <plumb.h>
11
#include "dat.h"
12
#include "edit.h"
13
#include "fns.h"
14
 
15
static char	linex[]="\n";
16
static char	wordx[]=" \t\n";
17
struct cmdtab cmdtab[]={
18
/*	cmdc	text	regexp	addr	defcmd	defaddr	count	token	 fn	*/
19
	'\n',	0,	0,	0,	0,	aDot,	0,	0,	nl_cmd,
20
	'a',	1,	0,	0,	0,	aDot,	0,	0,	a_cmd,
21
	'b',	0,	0,	0,	0,	aNo,	0,	linex,	b_cmd,
22
	'c',	1,	0,	0,	0,	aDot,	0,	0,	c_cmd,
23
	'd',	0,	0,	0,	0,	aDot,	0,	0,	d_cmd,
24
	'e',	0,	0,	0,	0,	aNo,	0,	wordx,	e_cmd,
25
	'f',	0,	0,	0,	0,	aNo,	0,	wordx,	f_cmd,
26
	'g',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
27
	'i',	1,	0,	0,	0,	aDot,	0,	0,	i_cmd,
28
	'm',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
29
	'p',	0,	0,	0,	0,	aDot,	0,	0,	p_cmd,
30
	'r',	0,	0,	0,	0,	aDot,	0,	wordx,	e_cmd,
31
	's',	0,	1,	0,	0,	aDot,	1,	0,	s_cmd,
32
	't',	0,	0,	1,	0,	aDot,	0,	0,	m_cmd,
33
	'u',	0,	0,	0,	0,	aNo,	2,	0,	u_cmd,
34
	'v',	0,	1,	0,	'p',	aDot,	0,	0,	g_cmd,
35
	'w',	0,	0,	0,	0,	aAll,	0,	wordx,	w_cmd,
36
	'x',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
37
	'y',	0,	1,	0,	'p',	aDot,	0,	0,	x_cmd,
38
	'=',	0,	0,	0,	0,	aDot,	0,	linex,	eq_cmd,
39
	'B',	0,	0,	0,	0,	aNo,	0,	linex,	B_cmd,
40
	'D',	0,	0,	0,	0,	aNo,	0,	linex,	D_cmd,
41
	'X',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
42
	'Y',	0,	1,	0,	'f',	aNo,	0,	0,	X_cmd,
43
	'<',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
44
	'|',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
45
	'>',	0,	0,	0,	0,	aDot,	0,	linex,	pipe_cmd,
46
/* deliberately unimplemented:
47
	'k',	0,	0,	0,	0,	aDot,	0,	0,	k_cmd,
48
	'n',	0,	0,	0,	0,	aNo,	0,	0,	n_cmd,
49
	'q',	0,	0,	0,	0,	aNo,	0,	0,	q_cmd,
50
	'!',	0,	0,	0,	0,	aNo,	0,	linex,	plan9_cmd,
51
 */
52
	0,	0,	0,	0,	0,	0,	0,	0,
53
};
54
 
55
Cmd	*parsecmd(int);
56
Addr	*compoundaddr(void);
57
Addr	*simpleaddr(void);
58
void	freecmd(void);
59
void	okdelim(int);
60
 
61
Rune	*cmdstartp;
62
Rune	*cmdendp;
63
Rune	*cmdp;
64
Channel	*editerrc;
65
 
66
String	*lastpat;
67
int	patset;
68
 
69
List	cmdlist;
70
List	addrlist;
71
List	stringlist;
72
Text	*curtext;
73
int	editing = Inactive;
74
 
75
String*	newstring(int);
76
 
77
void
78
editthread(void*)
79
{
80
	Cmd *cmdp;
81
 
82
	threadsetname("editthread");
83
	while((cmdp=parsecmd(0)) != 0){
84
//		ocurfile = curfile;
85
//		loaded = curfile && !curfile->unread;
86
		if(cmdexec(curtext, cmdp) == 0)
87
			break;
88
		freecmd();
89
	}
90
	sendp(editerrc, nil);
91
}
92
 
93
void
94
allelogterm(Window *w, void*)
95
{
96
	elogterm(w->body.file);
97
}
98
 
99
void
100
alleditinit(Window *w, void*)
101
{
102
	textcommit(&w->tag, TRUE);
103
	textcommit(&w->body, TRUE);
104
	w->body.file->editclean = FALSE;
105
}
106
 
107
void
108
allupdate(Window *w, void*)
109
{
110
	Text *t;
111
	int i;
112
	File *f;
113
 
114
	t = &w->body;
115
	f = t->file;
116
	if(f->curtext != t)	/* do curtext only */
117
		return;
118
	if(f->elog.type == Null)
119
		elogterm(f);
120
	else if(f->elog.type != Empty){
121
		elogapply(f);
122
		if(f->editclean){
123
			f->mod = FALSE;
124
			for(i=0; i<f->ntext; i++)
125
				f->text[i]->w->dirty = FALSE;
126
		}
127
	}
128
	textsetselect(t, t->q0, t->q1);
129
	textscrdraw(t);
130
	winsettag(w);
131
}
132
 
133
void
134
editerror(char *fmt, ...)
135
{
136
	va_list arg;
137
	char *s;
138
 
139
	va_start(arg, fmt);
140
	s = vsmprint(fmt, arg);
141
	va_end(arg);
142
	freecmd();
143
	allwindows(allelogterm, nil);	/* truncate the edit logs */
144
	sendp(editerrc, s);
145
	threadexits(nil);
146
}
147
 
148
void
149
editcmd(Text *ct, Rune *r, uint n)
150
{
151
	char *err;
152
 
153
	if(n == 0)
154
		return;
155
	if(2*n > RBUFSIZE){
156
		warning(nil, "string too long\n");
157
		return;
158
	}
159
 
160
	allwindows(alleditinit, nil);
161
	if(cmdstartp)
162
		free(cmdstartp);
163
	cmdstartp = runemalloc(n+2);
164
	runemove(cmdstartp, r, n);
165
	if(r[n] != '\n')
166
		cmdstartp[n++] = '\n';
167
	cmdstartp[n] = '\0';
168
	cmdendp = cmdstartp+n;
169
	cmdp = cmdstartp;
170
	if(ct->w == nil)
171
		curtext = nil;
172
	else
173
		curtext = &ct->w->body;
174
	resetxec();
175
	if(editerrc == nil){
176
		editerrc = chancreate(sizeof(char*), 0);
177
		lastpat = allocstring(0);
178
	}
179
	threadcreate(editthread, nil, STACK);
180
	err = recvp(editerrc);
181
	editing = Inactive;
182
	if(err != nil){
183
		if(err[0] != '\0')
184
			warning(nil, "Edit: %s\n", err);
185
		free(err);
186
	}
187
 
188
	/* update everyone whose edit log has data */
189
	allwindows(allupdate, nil);
190
}
191
 
192
int
193
getch(void)
194
{
195
	if(*cmdp == *cmdendp)
196
		return -1;
197
	return *cmdp++;
198
}
199
 
200
int
201
nextc(void)
202
{
203
	if(*cmdp == *cmdendp)
204
		return -1;
205
	return *cmdp;
206
}
207
 
208
void
209
ungetch(void)
210
{
211
	if(--cmdp < cmdstartp)
212
		error("ungetch");
213
}
214
 
215
long
216
getnum(int signok)
217
{
218
	long n;
219
	int c, sign;
220
 
221
	n = 0;
222
	sign = 1;
223
	if(signok>1 && nextc()=='-'){
224
		sign = -1;
225
		getch();
226
	}
227
	if((c=nextc())<'0' || '9'<c)	/* no number defaults to 1 */
228
		return sign;
229
	while('0'<=(c=getch()) && c<='9')
230
		n = n*10 + (c-'0');
231
	ungetch();
232
	return sign*n;
233
}
234
 
235
int
236
cmdskipbl(void)
237
{
238
	int c;
239
	do
240
		c = getch();
241
	while(c==' ' || c=='\t');
242
	if(c >= 0)
243
		ungetch();
244
	return c;
245
}
246
 
247
/*
248
 * Check that list has room for one more element.
249
 */
250
void
251
growlist(List *l)
252
{
253
	if(l->listptr==0 || l->nalloc==0){
254
		l->nalloc = INCR;
255
		l->listptr = emalloc(INCR*sizeof(void*));
256
		l->nused = 0;
257
	}else if(l->nused == l->nalloc){
258
		l->listptr = erealloc(l->listptr, (l->nalloc+INCR)*sizeof(void*));
259
		memset(l->ptr+l->nalloc, 0, INCR*sizeof(void*));
260
		l->nalloc += INCR;
261
	}
262
}
263
 
264
/*
265
 * Remove the ith element from the list
266
 */
267
void
268
dellist(List *l, int i)
269
{
270
	memmove(&l->ptr[i], &l->ptr[i+1], (l->nused-(i+1))*sizeof(void*));
271
	l->nused--;
272
}
273
 
274
/*
275
 * Add a new element, whose position is i, to the list
276
 */
277
void
278
inslist(List *l, int i, void *v)
279
{
280
	growlist(l);
281
	memmove(&l->ptr[i+1], &l->ptr[i], (l->nused-i)*sizeof(void*));
282
	l->ptr[i] = v;
283
	l->nused++;
284
}
285
 
286
void
287
listfree(List *l)
288
{
289
	free(l->listptr);
290
	free(l);
291
}
292
 
293
String*
294
allocstring(int n)
295
{
296
	String *s;
297
 
298
	s = emalloc(sizeof(String));
299
	s->n = n;
300
	s->nalloc = n+10;
301
	s->r = emalloc(s->nalloc*sizeof(Rune));
302
	s->r[n] = '\0';
303
	return s;
304
}
305
 
306
void
307
freestring(String *s)
308
{
309
	free(s->r);
310
	free(s);
311
}
312
 
313
Cmd*
314
newcmd(void){
315
	Cmd *p;
316
 
317
	p = emalloc(sizeof(Cmd));
318
	inslist(&cmdlist, cmdlist.nused, p);
319
	return p;
320
}
321
 
322
String*
323
newstring(int n)
324
{
325
	String *p;
326
 
327
	p = allocstring(n);
328
	inslist(&stringlist, stringlist.nused, p);
329
	return p;
330
}
331
 
332
Addr*
333
newaddr(void)
334
{
335
	Addr *p;
336
 
337
	p = emalloc(sizeof(Addr));
338
	inslist(&addrlist, addrlist.nused, p);
339
	return p;
340
}
341
 
342
void
343
freecmd(void)
344
{
345
	int i;
346
 
347
	while(cmdlist.nused > 0)
348
		free(cmdlist.ucharptr[--cmdlist.nused]);
349
	while(addrlist.nused > 0)
350
		free(addrlist.ucharptr[--addrlist.nused]);
351
	while(stringlist.nused>0){
352
		i = --stringlist.nused;
353
		freestring(stringlist.stringptr[i]);
354
	}
355
}
356
 
357
void
358
okdelim(int c)
359
{
360
	if(c=='\\' || ('a'<=c && c<='z')
361
	|| ('A'<=c && c<='Z') || ('0'<=c && c<='9'))
362
		editerror("bad delimiter %c\n", c);
363
}
364
 
365
void
366
atnl(void)
367
{
368
	int c;
369
 
370
	cmdskipbl();
371
	c = getch();
372
	if(c != '\n')
373
		editerror("newline expected (saw %C)", c);
374
}
375
 
376
void
377
Straddc(String *s, int c)
378
{
379
	if(s->n+1 >= s->nalloc){
380
		s->nalloc += 10;
381
		s->r = erealloc(s->r, s->nalloc*sizeof(Rune));
382
	}
383
	s->r[s->n++] = c;
384
	s->r[s->n] = '\0';
385
}
386
 
387
void
388
getrhs(String *s, int delim, int cmd)
389
{
390
	int c;
391
 
392
	while((c = getch())>0 && c!=delim && c!='\n'){
393
		if(c == '\\'){
394
			if((c=getch()) <= 0)
395
				error("bad right hand side");
396
			if(c == '\n'){
397
				ungetch();
398
				c='\\';
399
			}else if(c == 'n')
400
				c='\n';
401
			else if(c!=delim && (cmd=='s' || c!='\\'))	/* s does its own */
402
				Straddc(s, '\\');
403
		}
404
		Straddc(s, c);
405
	}
406
	ungetch();	/* let client read whether delimiter, '\n' or whatever */
407
}
408
 
409
String *
410
collecttoken(char *end)
411
{
412
	String *s = newstring(0);
413
	int c;
414
 
415
	while((c=nextc())==' ' || c=='\t')
416
		Straddc(s, getch()); /* blanks significant for getname() */
417
	while((c=getch())>0 && utfrune(end, c)==0)
418
		Straddc(s, c);
419
	if(c != '\n')
420
		atnl();
421
	return s;
422
}
423
 
424
String *
425
collecttext(void)
426
{
427
	String *s;
428
	int begline, i, c, delim;
429
 
430
	s = newstring(0);
431
	if(cmdskipbl()=='\n'){
432
		getch();
433
		i = 0;
434
		do{
435
			begline = i;
436
			while((c = getch())>0 && c!='\n')
437
				i++, Straddc(s, c);
438
			i++, Straddc(s, '\n');
439
			if(c < 0)
440
				goto Return;
441
		}while(s->r[begline]!='.' || s->r[begline+1]!='\n');
442
		s->r[s->n-2] = '\0';
443
		s->n -= 2;
444
	}else{
445
		okdelim(delim = getch());
446
		getrhs(s, delim, 'a');
447
		if(nextc()==delim)
448
			getch();
449
		atnl();
450
	}
451
    Return:
452
	return s;
453
}
454
 
455
int
456
cmdlookup(int c)
457
{
458
	int i;
459
 
460
	for(i=0; cmdtab[i].cmdc; i++)
461
		if(cmdtab[i].cmdc == c)
462
			return i;
463
	return -1;
464
}
465
 
466
Cmd*
467
parsecmd(int nest)
468
{
469
	int i, c;
470
	struct cmdtab *ct;
471
	Cmd *cp, *ncp;
472
	Cmd cmd;
473
 
474
	cmd.next = cmd.cmd = 0;
475
	cmd.re = 0;
476
	cmd.flag = cmd.num = 0;
477
	cmd.addr = compoundaddr();
478
	if(cmdskipbl() == -1)
479
		return 0;
480
	if((c=getch())==-1)
481
		return 0;
482
	cmd.cmdc = c;
483
	if(cmd.cmdc=='c' && nextc()=='d'){	/* sleazy two-character case */
484
		getch();		/* the 'd' */
485
		cmd.cmdc='c'|0x100;
486
	}
487
	i = cmdlookup(cmd.cmdc);
488
	if(i >= 0){
489
		if(cmd.cmdc == '\n')
490
			goto Return;	/* let nl_cmd work it all out */
491
		ct = &cmdtab[i];
492
		if(ct->defaddr==aNo && cmd.addr)
493
			editerror("command takes no address");
494
		if(ct->count)
495
			cmd.num = getnum(ct->count);
496
		if(ct->regexp){
497
			/* x without pattern -> .*\n, indicated by cmd.re==0 */
498
			/* X without pattern is all files */
499
			if((ct->cmdc!='x' && ct->cmdc!='X') ||
500
			   ((c = nextc())!=' ' && c!='\t' && c!='\n')){
501
				cmdskipbl();
502
				if((c = getch())=='\n' || c<0)
503
					editerror("no address");
504
				okdelim(c);
505
				cmd.re = getregexp(c);
506
				if(ct->cmdc == 's'){
507
					cmd.text = newstring(0);
508
					getrhs(cmd.text, c, 's');
509
					if(nextc() == c){
510
						getch();
511
						if(nextc() == 'g')
512
							cmd.flag = getch();
513
					}
514
 
515
				}
516
			}
517
		}
518
		if(ct->addr && (cmd.mtaddr=simpleaddr())==0)
519
			editerror("bad address");
520
		if(ct->defcmd){
521
			if(cmdskipbl() == '\n'){
522
				getch();
523
				cmd.cmd = newcmd();
524
				cmd.cmd->cmdc = ct->defcmd;
525
			}else if((cmd.cmd = parsecmd(nest))==0)
526
				error("defcmd");
527
		}else if(ct->text)
528
			cmd.text = collecttext();
529
		else if(ct->token)
530
			cmd.text = collecttoken(ct->token);
531
		else
532
			atnl();
533
	}else
534
		switch(cmd.cmdc){
535
		case '{':
536
			cp = 0;
537
			do{
538
				if(cmdskipbl()=='\n')
539
					getch();
540
				ncp = parsecmd(nest+1);
541
				if(cp)
542
					cp->next = ncp;
543
				else
544
					cmd.cmd = ncp;
545
			}while(cp = ncp);
546
			break;
547
		case '}':
548
			atnl();
549
			if(nest==0)
550
				editerror("right brace with no left brace");
551
			return 0;
552
		default:
553
			editerror("unknown command %c", cmd.cmdc);
554
		}
555
    Return:
556
	cp = newcmd();
557
	*cp = cmd;
558
	return cp;
559
}
560
 
561
String*
562
getregexp(int delim)
563
{
564
	String *buf, *r;
565
	int i, c;
566
 
567
	buf = allocstring(0);
568
	for(i=0; ; i++){
569
		if((c = getch())=='\\'){
570
			if(nextc()==delim)
571
				c = getch();
572
			else if(nextc()=='\\'){
573
				Straddc(buf, c);
574
				c = getch();
575
			}
576
		}else if(c==delim || c=='\n')
577
			break;
578
		if(i >= RBUFSIZE)
579
			editerror("regular expression too long");
580
		Straddc(buf, c);
581
	}
582
	if(c!=delim && c)
583
		ungetch();
584
	if(buf->n > 0){
585
		patset = TRUE;
586
		freestring(lastpat);
587
		lastpat = buf;
588
	}else
589
		freestring(buf);
590
	if(lastpat->n == 0)
591
		editerror("no regular expression defined");
592
	r = newstring(lastpat->n);
593
	runemove(r->r, lastpat->r, lastpat->n);	/* newstring put \0 at end */
594
	return r;
595
}
596
 
597
Addr *
598
simpleaddr(void)
599
{
600
	Addr addr;
601
	Addr *ap, *nap;
602
 
603
	addr.next = 0;
604
	addr.left = 0;
605
	switch(cmdskipbl()){
606
	case '#':
607
		addr.type = getch();
608
		addr.num = getnum(1);
609
		break;
610
	case '0': case '1': case '2': case '3': case '4':
611
	case '5': case '6': case '7': case '8': case '9': 
612
		addr.num = getnum(1);
613
		addr.type='l';
614
		break;
615
	case '/': case '?': case '"':
616
		addr.re = getregexp(addr.type = getch());
617
		break;
618
	case '.':
619
	case '$':
620
	case '+':
621
	case '-':
622
	case '\'':
623
		addr.type = getch();
624
		break;
625
	default:
626
		return 0;
627
	}
628
	if(addr.next = simpleaddr())
629
		switch(addr.next->type){
630
		case '.':
631
		case '$':
632
		case '\'':
633
			if(addr.type!='"')
634
		case '"':
635
				editerror("bad address syntax");
636
			break;
637
		case 'l':
638
		case '#':
639
			if(addr.type=='"')
640
				break;
641
			/* fall through */
642
		case '/':
643
		case '?':
644
			if(addr.type!='+' && addr.type!='-'){
645
				/* insert the missing '+' */
646
				nap = newaddr();
647
				nap->type='+';
648
				nap->next = addr.next;
649
				addr.next = nap;
650
			}
651
			break;
652
		case '+':
653
		case '-':
654
			break;
655
		default:
656
			error("simpleaddr");
657
		}
658
	ap = newaddr();
659
	*ap = addr;
660
	return ap;
661
}
662
 
663
Addr *
664
compoundaddr(void)
665
{
666
	Addr addr;
667
	Addr *ap, *next;
668
 
669
	addr.left = simpleaddr();
670
	if((addr.type = cmdskipbl())!=',' && addr.type!=';')
671
		return addr.left;
672
	getch();
673
	next = addr.next = compoundaddr();
674
	if(next && (next->type==',' || next->type==';') && next->left==0)
675
		editerror("bad address syntax");
676
	ap = newaddr();
677
	*ap = addr;
678
	return ap;
679
}