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 <regexp.h>
11
#include <plumb.h>
12
#include "dat.h"
13
#include "fns.h"
14
 
15
Window*	openfile(Text*, Expand*);
16
 
17
int	nuntitled;
18
 
19
void
20
look3(Text *t, uint q0, uint q1, int external)
21
{
22
	int n, c, f, expanded;
23
	Text *ct;
24
	Expand e;
25
	Rune *r;
26
	uint p;
27
	Plumbmsg *m;
28
	Runestr dir;
29
	char buf[32];
30
 
31
	ct = seltext;
32
	if(ct == nil)
33
		seltext = t;
34
	expanded = expand(t, q0, q1, &e);
35
	if(!external && t->w!=nil && t->w->nopen[QWevent]>0){
36
		/* send alphanumeric expansion to external client */
37
		if(expanded == FALSE)
38
			return;
39
		f = 0;
40
		if((e.at!=nil && t->w!=nil) || (e.nname>0 && lookfile(e.name, e.nname)!=nil))
41
			f = 1;		/* acme can do it without loading a file */
42
		if(q0!=e.q0 || q1!=e.q1)
43
			f |= 2;	/* second (post-expand) message follows */
44
		if(e.nname)
45
			f |= 4;	/* it's a file name */
46
		c = 'l';
47
		if(t->what == Body)
48
			c = 'L';
49
		n = q1-q0;
50
		if(n <= EVENTSIZE){
51
			r = runemalloc(n);
52
			bufread(t->file, q0, r, n);
53
			winevent(t->w, "%c%d %d %d %d %.*S\n", c, q0, q1, f, n, n, r);
54
			free(r);
55
		}else
56
			winevent(t->w, "%c%d %d %d 0 \n", c, q0, q1, f, n);
57
		if(q0==e.q0 && q1==e.q1)
58
			return;
59
		if(e.nname){
60
			n = e.nname;
61
			if(e.a1 > e.a0)
62
				n += 1+(e.a1-e.a0);
63
			r = runemalloc(n);
64
			runemove(r, e.name, e.nname);
65
			if(e.a1 > e.a0){
66
				r[e.nname] = ':';
67
				bufread(e.at->file, e.a0, r+e.nname+1, e.a1-e.a0);
68
			}
69
		}else{
70
			n = e.q1 - e.q0;
71
			r = runemalloc(n);
72
			bufread(t->file, e.q0, r, n);
73
		}
74
		f &= ~2;
75
		if(n <= EVENTSIZE)
76
			winevent(t->w, "%c%d %d %d %d %.*S\n", c, e.q0, e.q1, f, n, n, r);
77
		else
78
			winevent(t->w, "%c%d %d %d 0 \n", c, e.q0, e.q1, f, n);
79
		free(r);
80
		goto Return;
81
	}
82
	if(plumbsendfd >= 0){
83
		/* send whitespace-delimited word to plumber */
84
		m = emalloc(sizeof(Plumbmsg));
85
		m->src = estrdup("acme");
86
		m->dst = nil;
87
		dir = dirname(t, nil, 0);
88
		if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
89
			free(dir.r);
90
			dir.r = nil;
91
			dir.nr = 0;
92
		}
93
		if(dir.nr == 0)
94
			m->wdir = estrdup(wdir);
95
		else
96
			m->wdir = runetobyte(dir.r, dir.nr);
97
		free(dir.r);
98
		m->type = estrdup("text");
99
		m->attr = nil;
100
		buf[0] = '\0';
101
		if(q1 == q0){
102
			if(t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
103
				q0 = t->q0;
104
				q1 = t->q1;
105
			}else{
106
				p = q0;
107
				while(q0>0 && (c=tgetc(t, q0-1))!=' ' && c!='\t' && c!='\n')
108
					q0--;
109
				while(q1<t->file->nc && (c=tgetc(t, q1))!=' ' && c!='\t' && c!='\n')
110
					q1++;
111
				if(q1 == q0){
112
					plumbfree(m);
113
					goto Return;
114
				}
115
				sprint(buf, "click=%d", p-q0);
116
				m->attr = plumbunpackattr(buf);
117
			}
118
		}
119
		r = runemalloc(q1-q0);
120
		bufread(t->file, q0, r, q1-q0);
121
		m->data = runetobyte(r, q1-q0);
122
		m->ndata = strlen(m->data);
123
		free(r);
124
		if(m->ndata<messagesize-1024 && plumbsend(plumbsendfd, m) >= 0){
125
			plumbfree(m);
126
			goto Return;
127
		}
128
		plumbfree(m);
129
		/* plumber failed to match; fall through */
130
	}
131
 
132
	/* interpret alphanumeric string ourselves */
133
	if(expanded == FALSE)
134
		return;
135
	if(e.name || e.at)
136
		openfile(t, &e);
137
	else{
138
		if(t->w == nil)
139
			return;
140
		ct = &t->w->body;
141
		if(t->w != ct->w)
142
			winlock(ct->w, 'M');
143
		if(t == ct)
144
			textsetselect(ct, e.q1, e.q1);
145
		n = e.q1 - e.q0;
146
		r = runemalloc(n);
147
		bufread(t->file, e.q0, r, n);
148
		if(search(ct, r, n) && e.jump)
149
			moveto(mousectl, addpt(frptofchar(ct, ct->p0), Pt(4, ct->font->height-4)));
150
		if(t->w != ct->w)
151
			winunlock(ct->w);
152
		free(r);
153
	}
154
 
155
   Return:
156
	free(e.name);
157
	free(e.bname);
158
}
159
 
160
int
161
plumbgetc(void *a, uint n)
162
{
163
	Rune *r;
164
 
165
	r = a;
166
	if(n>runestrlen(r))
167
		return 0;
168
	return r[n];
169
}
170
 
171
void
172
plumblook(Plumbmsg *m)
173
{
174
	Expand e;
175
	char *addr;
176
 
177
	if(m->ndata >= BUFSIZE){
178
		warning(nil, "insanely long file name (%d bytes) in plumb message (%.32s...)\n", m->ndata, m->data);
179
		return;
180
	}
181
	e.q0 = 0;
182
	e.q1 = 0;
183
	if(m->data[0] == '\0')
184
		return;
185
	e.ar = nil;
186
	e.bname = m->data;
187
	e.name = bytetorune(e.bname, &e.nname);
188
	e.jump = TRUE;
189
	e.a0 = 0;
190
	e.a1 = 0;
191
	addr = plumblookup(m->attr, "addr");
192
	if(addr != nil){
193
		e.ar = bytetorune(addr, &e.a1);
194
		e.agetc = plumbgetc;
195
	}
196
	openfile(nil, &e);
197
	free(e.name);
198
	free(e.at);
199
}
200
 
201
void
202
plumbshow(Plumbmsg *m)
203
{
204
	Window *w;
205
	Rune rb[256], *r;
206
	int nb, nr;
207
	Runestr rs;
208
	char *name, *p, namebuf[16];
209
 
210
	w = makenewwindow(nil);
211
	name = plumblookup(m->attr, "filename");
212
	if(name == nil){
213
		name = namebuf;
214
		nuntitled++;
215
		snprint(namebuf, sizeof namebuf, "Untitled-%d", nuntitled);
216
	}
217
	p = nil;
218
	if(name[0]!='/' && m->wdir!=nil && m->wdir[0]!='\0'){
219
		nb = strlen(m->wdir) + 1 + strlen(name) + 1;
220
		p = emalloc(nb);
221
		snprint(p, nb, "%s/%s", m->wdir, name);
222
		name = p;
223
	}
224
	cvttorunes(name, strlen(name), rb, &nb, &nr, nil);
225
	free(p);
226
	rs = cleanrname((Runestr){rb, nr});
227
	winsetname(w, rs.r, rs.nr);
228
	r = runemalloc(m->ndata);
229
	cvttorunes(m->data, m->ndata, r, &nb, &nr, nil);
230
	textinsert(&w->body, 0, r, nr, TRUE);
231
	free(r);
232
	w->body.file->mod = FALSE;
233
	w->dirty = FALSE;
234
	winsettag(w);
235
	textscrdraw(&w->body);
236
	textsetselect(&w->tag, w->tag.file->nc, w->tag.file->nc);
237
}
238
 
239
int
240
search(Text *ct, Rune *r, uint n)
241
{
242
	uint q, nb, maxn;
243
	int around;
244
	Rune *s, *b, *c;
245
 
246
	if(n==0 || n>ct->file->nc)
247
		return FALSE;
248
	if(2*n > RBUFSIZE){
249
		warning(nil, "string too long\n");
250
		return FALSE;
251
	}
252
	maxn = max(2*n, RBUFSIZE);
253
	s = fbufalloc();
254
	b = s;
255
	nb = 0;
256
	b[nb] = 0;
257
	around = 0;
258
	q = ct->q1;
259
	for(;;){
260
		if(q >= ct->file->nc){
261
			q = 0;
262
			around = 1;
263
			nb = 0;
264
			b[nb] = 0;
265
		}
266
		if(nb > 0){
267
			c = runestrchr(b, r[0]);
268
			if(c == nil){
269
				q += nb;
270
				nb = 0;
271
				b[nb] = 0;
272
				if(around && q>=ct->q1)
273
					break;
274
				continue;
275
			}
276
			q += (c-b);
277
			nb -= (c-b);
278
			b = c;
279
		}
280
		/* reload if buffer covers neither string nor rest of file */
281
		if(nb<n && nb!=ct->file->nc-q){
282
			nb = ct->file->nc-q;
283
			if(nb >= maxn)
284
				nb = maxn-1;
285
			bufread(ct->file, q, s, nb);
286
			b = s;
287
			b[nb] = '\0';
288
		}
289
		/* this runeeq is fishy but the null at b[nb] makes it safe */
290
		if(runeeq(b, n, r, n)==TRUE){
291
			if(ct->w){
292
				textshow(ct, q, q+n, 1);
293
				winsettag(ct->w);
294
			}else{
295
				ct->q0 = q;
296
				ct->q1 = q+n;
297
			}
298
			seltext = ct;
299
			fbuffree(s);
300
			return TRUE;
301
		}
302
		--nb;
303
		b++;
304
		q++;
305
		if(around && q>=ct->q1)
306
			break;
307
	}
308
	fbuffree(s);
309
	return FALSE;
310
}
311
 
312
int
313
isfilec(Rune r)
314
{
315
	if(isalnum(r))
316
		return TRUE;
317
	if(runestrchr(L".-+/:", r))
318
		return TRUE;
319
	return FALSE;
320
}
321
 
322
/* Runestr wrapper for cleanname */
323
Runestr
324
cleanrname(Runestr rs)
325
{
326
	char *s;
327
	int nb, nulls;
328
 
329
	s = runetobyte(rs.r, rs.nr);
330
	cleanname(s);
331
	cvttorunes(s, strlen(s), rs.r, &nb, &rs.nr, &nulls);
332
	free(s);
333
	return rs;
334
}
335
 
336
Runestr
337
includefile(Rune *dir, Rune *file, int nfile)
338
{
339
	int m, n;
340
	char *a;
341
	Rune *r;
342
 
343
	m = runestrlen(dir);
344
	a = emalloc((m+1+nfile)*UTFmax+1);
345
	sprint(a, "%S/%.*S", dir, nfile, file);
346
	n = access(a, 0);
347
	free(a);
348
	if(n < 0)
349
		return (Runestr){nil, 0};
350
	r = runemalloc(m+1+nfile);
351
	runemove(r, dir, m);
352
	runemove(r+m, L"/", 1);
353
	runemove(r+m+1, file, nfile);
354
	free(file);
355
	return cleanrname((Runestr){r, m+1+nfile});
356
}
357
 
358
static	Rune	*objdir;
359
 
360
Runestr
361
includename(Text *t, Rune *r, int n)
362
{
363
	Window *w;
364
	char buf[128];
365
	Runestr file;
366
	int i;
367
 
368
	if(objdir==nil && objtype!=nil){
369
		sprint(buf, "/%s/include", objtype);
370
		objdir = bytetorune(buf, &i);
371
		objdir = runerealloc(objdir, i+1);
372
		objdir[i] = '\0';	
373
	}
374
 
375
	w = t->w;
376
	if(n==0 || r[0]=='/' || w==nil)
377
		goto Rescue;
378
	if(n>2 && r[0]=='.' && r[1]=='/')
379
		goto Rescue;
380
	file.r = nil;
381
	file.nr = 0;
382
	for(i=0; i<w->nincl && file.r==nil; i++)
383
		file = includefile(w->incl[i], r, n);
384
 
385
	if(file.r == nil)
386
		file = includefile(L"/sys/include", r, n);
387
	if(file.r==nil && objdir!=nil)
388
		file = includefile(objdir, r, n);
389
	if(file.r == nil)
390
		goto Rescue;
391
	return file;
392
 
393
    Rescue:
394
	return (Runestr){r, n};
395
}
396
 
397
Runestr
398
dirname(Text *t, Rune *r, int n)
399
{
400
	Rune *b, c;
401
	uint m, nt;
402
	int slash;
403
	Runestr tmp;
404
 
405
	b = nil;
406
	if(t==nil || t->w==nil)
407
		goto Rescue;
408
	nt = t->w->tag.file->nc;
409
	if(nt == 0)
410
		goto Rescue;
411
	if(n>=1 && r[0]=='/')
412
		goto Rescue;
413
	b = runemalloc(nt+n+1);
414
	bufread(t->w->tag.file, 0, b, nt);
415
	slash = -1;
416
	for(m=0; m<nt; m++){
417
		c = b[m];
418
		if(c == '/')
419
			slash = m;
420
		if(c==' ' || c=='\t')
421
			break;
422
	}
423
	if(slash < 0)
424
		goto Rescue;
425
	runemove(b+slash+1, r, n);
426
	free(r);
427
	return cleanrname((Runestr){b, slash+1+n});
428
 
429
    Rescue:
430
	free(b);
431
	tmp = (Runestr){r, n};
432
	if(r)
433
		return cleanrname(tmp);
434
	return tmp;
435
}
436
 
437
int
438
expandfile(Text *t, uint q0, uint q1, Expand *e)
439
{
440
	int i, n, nname, colon, eval;
441
	uint amin, amax;
442
	Rune *r, c;
443
	Window *w;
444
	Runestr rs;
445
 
446
	amax = q1;
447
	if(q1 == q0){
448
		colon = -1;
449
		while(q1<t->file->nc && isfilec(c=textreadc(t, q1))){
450
			if(c == ':'){
451
				colon = q1;
452
				break;
453
			}
454
			q1++;
455
		}
456
		while(q0>0 && (isfilec(c=textreadc(t, q0-1)) || isaddrc(c) || isregexc(c))){
457
			q0--;
458
			if(colon<0 && c==':')
459
				colon = q0;
460
		}
461
		/*
462
		 * if it looks like it might begin file: , consume address chars after :
463
		 * otherwise terminate expansion at :
464
		 */
465
		if(colon >= 0){
466
			q1 = colon;
467
			if(colon<t->file->nc-1 && isaddrc(textreadc(t, colon+1))){
468
				q1 = colon+1;
469
				while(q1<t->file->nc && isaddrc(textreadc(t, q1)))
470
					q1++;
471
			}
472
		}
473
		if(q1 > q0)
474
			if(colon >= 0){	/* stop at white space */
475
				for(amax=colon+1; amax<t->file->nc; amax++)
476
					if((c=textreadc(t, amax))==' ' || c=='\t' || c=='\n')
477
						break;
478
			}else
479
				amax = t->file->nc;
480
	}
481
	amin = amax;
482
	e->q0 = q0;
483
	e->q1 = q1;
484
	n = q1-q0;
485
	if(n == 0)
486
		return FALSE;
487
	/* see if it's a file name */
488
	r = runemalloc(n);
489
	bufread(t->file, q0, r, n);
490
	/* first, does it have bad chars? */
491
	nname = -1;
492
	for(i=0; i<n; i++){
493
		c = r[i];
494
		if(c==':' && nname<0){
495
			if(q0+i+1<t->file->nc && (i==n-1 || isaddrc(textreadc(t, q0+i+1))))
496
				amin = q0+i;
497
			else
498
				goto Isntfile;
499
			nname = i;
500
		}
501
	}
502
	if(nname == -1)
503
		nname = n;
504
	for(i=0; i<nname; i++)
505
		if(!isfilec(r[i]))
506
			goto Isntfile;
507
	/*
508
	 * See if it's a file name in <>, and turn that into an include
509
	 * file name if so.  Should probably do it for "" too, but that's not
510
	 * restrictive enough syntax and checking for a #include earlier on the
511
	 * line would be silly.
512
	 */
513
	if(q0>0 && textreadc(t, q0-1)=='<' && q1<t->file->nc && textreadc(t, q1)=='>'){
514
		rs = includename(t, r, nname);
515
		r = rs.r;
516
		nname = rs.nr;
517
	}
518
	else if(amin == q0)
519
		goto Isfile;
520
	else{
521
		rs = dirname(t, r, nname);
522
		r = rs.r;
523
		nname = rs.nr;
524
	}
525
	e->bname = runetobyte(r, nname);
526
	/* if it's already a window name, it's a file */
527
	w = lookfile(r, nname);
528
	if(w != nil)
529
		goto Isfile;
530
	/* if it's the name of a file, it's a file */
531
	if(access(e->bname, 0) < 0){
532
		free(e->bname);
533
		e->bname = nil;
534
		goto Isntfile;
535
	}
536
 
537
  Isfile:
538
	e->name = r;
539
	e->nname = nname;
540
	e->at = t;
541
	e->a0 = amin+1;
542
	eval = FALSE;
543
	address(nil, nil, (Range){-1,-1}, (Range){0, 0}, t, e->a0, amax, tgetc, &eval, (uint*)&e->a1);
544
	return TRUE;
545
 
546
   Isntfile:
547
	free(r);
548
	return FALSE;
549
}
550
 
551
int
552
expand(Text *t, uint q0, uint q1, Expand *e)
553
{
554
	memset(e, 0, sizeof *e);
555
	e->agetc = tgetc;
556
	/* if in selection, choose selection */
557
	e->jump = TRUE;
558
	if(q1==q0 && t->q1>t->q0 && t->q0<=q0 && q0<=t->q1){
559
		q0 = t->q0;
560
		q1 = t->q1;
561
		if(t->what == Tag)
562
			e->jump = FALSE;
563
	}
564
 
565
	if(expandfile(t, q0, q1, e))
566
		return TRUE;
567
 
568
	if(q0 == q1){
569
		while(q1<t->file->nc && isalnum(textreadc(t, q1)))
570
			q1++;
571
		while(q0>0 && isalnum(textreadc(t, q0-1)))
572
			q0--;
573
	}
574
	e->q0 = q0;
575
	e->q1 = q1;
576
	return q1 > q0;
577
}
578
 
579
Window*
580
lookfile(Rune *s, int n)
581
{
582
	int i, j, k;
583
	Window *w;
584
	Column *c;
585
	Text *t;
586
 
587
	/* avoid terminal slash on directories */
588
	if(n>1 && s[n-1] == '/')
589
		--n;
590
	for(j=0; j<row.ncol; j++){
591
		c = row.col[j];
592
		for(i=0; i<c->nw; i++){
593
			w = c->w[i];
594
			t = &w->body;
595
			k = t->file->nname;
596
			if(k>1 && t->file->name[k-1] == '/')
597
				k--;
598
			if(runeeq(t->file->name, k, s, n)){
599
				w = w->body.file->curtext->w;
600
				if(w->col != nil)	/* protect against race deleting w */
601
					return w;
602
			}
603
		}
604
	}
605
	return nil;
606
}
607
 
608
Window*
609
lookid(int id, int dump)
610
{
611
	int i, j;
612
	Window *w;
613
	Column *c;
614
 
615
	for(j=0; j<row.ncol; j++){
616
		c = row.col[j];
617
		for(i=0; i<c->nw; i++){
618
			w = c->w[i];
619
			if(dump && w->dumpid == id)
620
				return w;
621
			if(!dump && w->id == id)
622
				return w;
623
		}
624
	}
625
	return nil;
626
}
627
 
628
 
629
Window*
630
openfile(Text *t, Expand *e)
631
{
632
	Range r;
633
	Window *w, *ow;
634
	int eval, i, n;
635
	Rune *rp;
636
	uint dummy;
637
 
638
	if(e->nname == 0){
639
		w = t->w;
640
		if(w == nil)
641
			return nil;
642
	}else
643
		w = lookfile(e->name, e->nname);
644
	if(w){
645
		t = &w->body;
646
		if(!t->col->safe && t->maxlines==0) /* window is obscured by full-column window */
647
			colgrow(t->col, t->col->w[0], 1);
648
	}else{
649
		ow = nil;
650
		if(t)
651
			ow = t->w;
652
		w = makenewwindow(t);
653
		t = &w->body;
654
		winsetname(w, e->name, e->nname);
655
		textload(t, 0, e->bname, 1);
656
		t->file->mod = FALSE;
657
		t->w->dirty = FALSE;
658
		winsettag(t->w);
659
		textsetselect(&t->w->tag, t->w->tag.file->nc, t->w->tag.file->nc);
660
		if(ow != nil){
661
			for(i=ow->nincl; --i>=0; ){
662
				n = runestrlen(ow->incl[i]);
663
				rp = runemalloc(n);
664
				runemove(rp, ow->incl[i], n);
665
				winaddincl(w, rp, n);
666
			}
667
			w->autoindent = ow->autoindent;
668
		}else
669
			w->autoindent = globalautoindent;
670
	}
671
	if(e->a1 == e->a0)
672
		eval = FALSE;
673
	else{
674
		eval = TRUE;
675
		r = address(nil, t, (Range){-1, -1}, (Range){t->q0, t->q1}, e->at, e->a0, e->a1, e->agetc, &eval, &dummy);
676
		if(eval == FALSE)
677
			e->jump = FALSE;	/* don't jump if invalid address */
678
	}
679
	if(eval == FALSE){
680
		r.q0 = t->q0;
681
		r.q1 = t->q1;
682
	}
683
	textshow(t, r.q0, r.q1, 1);
684
	winsettag(t->w);
685
	seltext = t;
686
	if(e->jump)
687
		moveto(mousectl, addpt(frptofchar(t, t->p0), Pt(4, font->height-4)));
688
	return w;
689
}
690
 
691
void
692
new(Text *et, Text *t, Text *argt, int flag1, int flag2, Rune *arg, int narg)
693
{
694
	int ndone;
695
	Rune *a, *f;
696
	int na, nf;
697
	Expand e;
698
	Runestr rs;
699
 
700
	getarg(argt, FALSE, TRUE, &a, &na);
701
	if(a){
702
		new(et, t, nil, flag1, flag2, a, na);
703
		if(narg == 0)
704
			return;
705
	}
706
	/* loop condition: *arg is not a blank */
707
	for(ndone=0; ; ndone++){
708
		a = findbl(arg, narg, &na);
709
		if(a == arg){
710
			if(ndone==0 && et->col!=nil)
711
				winsettag(coladd(et->col, nil, nil, -1));
712
			break;
713
		}
714
		nf = narg-na;
715
		f = runemalloc(nf);
716
		runemove(f, arg, nf);
717
		rs = dirname(et, f, nf);
718
		f = rs.r;
719
		nf = rs.nr;
720
		memset(&e, 0, sizeof e);
721
		e.name = f;
722
		e.nname = nf;
723
		e.bname = runetobyte(f, nf);
724
		e.jump = TRUE;
725
		openfile(et, &e);
726
		free(f);
727
		free(e.bname);
728
		arg = skipbl(a, na, &narg);
729
	}
730
}