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 <libsec.h>
12 #include "dat.h"
13 #include "fns.h"
14 
15 static	Point		prevmouse;
16 static	Window	*mousew;
17 
18 Range
range(int q0,int q1)19 range(int q0, int q1)
20 {
21 	Range r;
22 
23 	r.q0 = q0;
24 	r.q1 = q1;
25 	return r;
26 }
27 
28 Runestr
runestr(Rune * r,uint n)29 runestr(Rune *r, uint n)
30 {
31 	Runestr rs;
32 
33 	rs.r = r;
34 	rs.nr = n;
35 	return rs;
36 }
37 
38 void
cvttorunes(char * p,int n,Rune * r,int * nb,int * nr,int * nulls)39 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
40 {
41 	uchar *q;
42 	Rune *s;
43 	int j, w;
44 
45 	/*
46 	 * Always guaranteed that n bytes may be interpreted
47 	 * without worrying about partial runes.  This may mean
48 	 * reading up to UTFmax-1 more bytes than n; the caller
49 	 * knows this.  If n is a firm limit, the caller should
50 	 * set p[n] = 0.
51 	 */
52 	q = (uchar*)p;
53 	s = r;
54 	for(j=0; j<n; j+=w){
55 		if(*q < Runeself){
56 			w = 1;
57 			*s = *q++;
58 		}else{
59 			w = chartorune(s, (char*)q);
60 			q += w;
61 		}
62 		if(*s)
63 			s++;
64 		else if(nulls)
65 			*nulls = TRUE;
66 	}
67 	*nb = (char*)q-p;
68 	*nr = s-r;
69 }
70 
71 void
error(char * s)72 error(char *s)
73 {
74 	fprint(2, "acme: %s: %r\n", s);
75 	threadexitsall(nil);
76 }
77 
78 Window*
errorwin1(Rune * dir,int ndir,Rune ** incl,int nincl)79 errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
80 {
81 	Window *w;
82 	Rune *r;
83 	int i, n;
84 	static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
85 
86 	r = runemalloc(ndir+8);
87 	if((n = ndir) != 0){
88 		runemove(r, dir, ndir);
89 		r[n++] = L'/';
90 	}
91 	runemove(r+n, Lpluserrors, 7);
92 	n += 7;
93 	w = lookfile(r, n);
94 	if(w == nil){
95 		if(row.ncol == 0)
96 			if(rowadd(&row, nil, -1) == nil)
97 				error("can't create column to make error window");
98 		w = coladd(row.col[row.ncol-1], nil, nil, -1);
99 		w->filemenu = FALSE;
100 		winsetname(w, r, n);
101 		xfidlog(w, "new");
102 	}
103 	free(r);
104 	for(i=nincl; --i>=0; ){
105 		n = runestrlen(incl[i]);
106 		r = runemalloc(n);
107 		runemove(r, incl[i], n);
108 		winaddincl(w, r, n);
109 	}
110 	w->autoindent = globalautoindent;
111 	return w;
112 }
113 
114 /* make new window, if necessary; return with it locked */
115 Window*
errorwin(Mntdir * md,int owner)116 errorwin(Mntdir *md, int owner)
117 {
118 	Window *w;
119 
120 	for(;;){
121 		if(md == nil)
122 			w = errorwin1(nil, 0, nil, 0);
123 		else
124 			w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
125 		winlock(w, owner);
126 		if(w->col != nil)
127 			break;
128 		/* window was deleted too fast */
129 		winunlock(w);
130 	}
131 	return w;
132 }
133 
134 /*
135  * Incoming window should be locked.
136  * It will be unlocked and returned window
137  * will be locked in its place.
138  */
139 Window*
errorwinforwin(Window * w)140 errorwinforwin(Window *w)
141 {
142 	int i, n, nincl, owner;
143 	Rune **incl;
144 	Runestr dir;
145 	Text *t;
146 
147 	t = &w->body;
148 	dir = dirname(t, nil, 0);
149 	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */
150 		free(dir.r);
151 		dir.r = nil;
152 		dir.nr = 0;
153 	}
154 	incl = nil;
155 	nincl = w->nincl;
156 	if(nincl > 0){
157 		incl = emalloc(nincl*sizeof(Rune*));
158 		for(i=0; i<nincl; i++){
159 			n = runestrlen(w->incl[i]);
160 			incl[i] = runemalloc(n+1);
161 			runemove(incl[i], w->incl[i], n);
162 		}
163 	}
164 	owner = w->owner;
165 	winunlock(w);
166 	for(;;){
167 		w = errorwin1(dir.r, dir.nr, incl, nincl);
168 		winlock(w, owner);
169 		if(w->col != nil)
170 			break;
171 		/* window deleted too fast */
172 		winunlock(w);
173 	}
174 	return w;
175 }
176 
177 typedef struct Warning Warning;
178 
179 struct Warning{
180 	Mntdir *md;
181 	Buffer buf;
182 	Warning *next;
183 };
184 
185 static Warning *warnings;
186 
187 static
188 void
addwarningtext(Mntdir * md,Rune * r,int nr)189 addwarningtext(Mntdir *md, Rune *r, int nr)
190 {
191 	Warning *warn;
192 
193 	for(warn = warnings; warn; warn=warn->next){
194 		if(warn->md == md){
195 			bufinsert(&warn->buf, warn->buf.nc, r, nr);
196 			return;
197 		}
198 	}
199 	warn = emalloc(sizeof(Warning));
200 	warn->next = warnings;
201 	warn->md = md;
202 	if(md)
203 		fsysincid(md);
204 	warnings = warn;
205 	bufinsert(&warn->buf, 0, r, nr);
206 	nbsendp(cwarn, 0);
207 }
208 
209 /* called while row is locked */
210 void
flushwarnings(void)211 flushwarnings(void)
212 {
213 	Warning *warn, *next;
214 	Window *w;
215 	Text *t;
216 	int owner, nr, q0, n;
217 	Rune *r;
218 
219 	for(warn=warnings; warn; warn=next) {
220 		w = errorwin(warn->md, 'E');
221 		t = &w->body;
222 		owner = w->owner;
223 		if(owner == 0)
224 			w->owner = 'E';
225 		wincommit(w, t);
226 		/*
227 		 * Most commands don't generate much output. For instance,
228 		 * Edit ,>cat goes through /dev/cons and is already in blocks
229 		 * because of the i/o system, but a few can.  Edit ,p will
230 		 * put the entire result into a single hunk.  So it's worth doing
231 		 * this in blocks (and putting the text in a buffer in the first
232 		 * place), to avoid a big memory footprint.
233 		 */
234 		r = fbufalloc();
235 		q0 = t->file->b.nc;
236 		for(n = 0; n < warn->buf.nc; n += nr){
237 			nr = warn->buf.nc - n;
238 			if(nr > RBUFSIZE)
239 				nr = RBUFSIZE;
240 			bufread(&warn->buf, n, r, nr);
241 			textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
242 		}
243 		textshow(t, q0, t->file->b.nc, 1);
244 		free(r);
245 		winsettag(t->w);
246 		textscrdraw(t);
247 		w->owner = owner;
248 		w->dirty = FALSE;
249 		winunlock(w);
250 		bufclose(&warn->buf);
251 		next = warn->next;
252 		if(warn->md)
253 			fsysdelid(warn->md);
254 		free(warn);
255 	}
256 	warnings = nil;
257 }
258 
259 void
warning(Mntdir * md,char * s,...)260 warning(Mntdir *md, char *s, ...)
261 {
262 	Rune *r;
263 	va_list arg;
264 
265 	va_start(arg, s);
266 	r = runevsmprint(s, arg);
267 	va_end(arg);
268 	if(r == nil)
269 		error("runevsmprint failed");
270 	addwarningtext(md, r, runestrlen(r));
271 	free(r);
272 }
273 
274 int
runeeq(Rune * s1,uint n1,Rune * s2,uint n2)275 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
276 {
277 	if(n1 != n2)
278 		return FALSE;
279 	if(n1 == 0)
280 		return TRUE;
281 	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
282 }
283 
284 uint
min(uint a,uint b)285 min(uint a, uint b)
286 {
287 	if(a < b)
288 		return a;
289 	return b;
290 }
291 
292 uint
max(uint a,uint b)293 max(uint a, uint b)
294 {
295 	if(a > b)
296 		return a;
297 	return b;
298 }
299 
300 char*
runetobyte(Rune * r,int n)301 runetobyte(Rune *r, int n)
302 {
303 	char *s;
304 
305 	if(r == nil)
306 		return nil;
307 	s = emalloc(n*UTFmax+1);
308 	setmalloctag(s, getcallerpc(&r));
309 	snprint(s, n*UTFmax+1, "%.*S", n, r);
310 	return s;
311 }
312 
313 Rune*
bytetorune(char * s,int * ip)314 bytetorune(char *s, int *ip)
315 {
316 	Rune *r;
317 	int nb, nr;
318 
319 	nb = strlen(s);
320 	r = runemalloc(nb+1);
321 	cvttorunes(s, nb, r, &nb, &nr, nil);
322 	r[nr] = '\0';
323 	*ip = nr;
324 	return r;
325 }
326 
327 int
isalnum(Rune c)328 isalnum(Rune c)
329 {
330 	/*
331 	 * Hard to get absolutely right.  Use what we know about ASCII
332 	 * and assume anything above the Latin control characters is
333 	 * potentially an alphanumeric.
334 	 */
335 	if(c <= ' ')
336 		return FALSE;
337 	if(0x7F<=c && c<=0xA0)
338 		return FALSE;
339 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
340 		return FALSE;
341 	return TRUE;
342 }
343 
344 int
rgetc(void * v,uint n)345 rgetc(void *v, uint n)
346 {
347 	return ((Rune*)v)[n];
348 }
349 
350 int
tgetc(void * a,uint n)351 tgetc(void *a, uint n)
352 {
353 	Text *t;
354 
355 	t = a;
356 	if(n >= t->file->b.nc)
357 		return 0;
358 	return textreadc(t, n);
359 }
360 
361 Rune*
skipbl(Rune * r,int n,int * np)362 skipbl(Rune *r, int n, int *np)
363 {
364 	while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
365 		--n;
366 		r++;
367 	}
368 	*np = n;
369 	return r;
370 }
371 
372 Rune*
findbl(Rune * r,int n,int * np)373 findbl(Rune *r, int n, int *np)
374 {
375 	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
376 		--n;
377 		r++;
378 	}
379 	*np = n;
380 	return r;
381 }
382 
383 void
savemouse(Window * w)384 savemouse(Window *w)
385 {
386 	prevmouse = mouse->xy;
387 	mousew = w;
388 }
389 
390 int
restoremouse(Window * w)391 restoremouse(Window *w)
392 {
393 	int did;
394 
395 	did = 0;
396 	if(mousew!=nil && mousew==w) {
397 		moveto(mousectl, prevmouse);
398 		did = 1;
399 	}
400 	mousew = nil;
401 	return did;
402 }
403 
404 void
clearmouse()405 clearmouse()
406 {
407 	mousew = nil;
408 }
409 
410 char*
estrdup(char * s)411 estrdup(char *s)
412 {
413 	char *t;
414 
415 	t = strdup(s);
416 	if(t == nil)
417 		error("strdup failed");
418 	setmalloctag(t, getcallerpc(&s));
419 	return t;
420 }
421 
422 void*
emalloc(uint n)423 emalloc(uint n)
424 {
425 	void *p;
426 
427 	p = malloc(n);
428 	if(p == nil)
429 		error("malloc failed");
430 	setmalloctag(p, getcallerpc(&n));
431 	memset(p, 0, n);
432 	return p;
433 }
434 
435 void*
erealloc(void * p,uint n)436 erealloc(void *p, uint n)
437 {
438 	p = realloc(p, n);
439 	if(p == nil)
440 		error("realloc failed");
441 	setmalloctag(p, getcallerpc(&n));
442 	return p;
443 }
444 
445 /*
446  * Heuristic city.
447  */
448 Window*
makenewwindow(Text * t)449 makenewwindow(Text *t)
450 {
451 	Column *c;
452 	Window *w, *bigw, *emptyw;
453 	Text *emptyb;
454 	int i, y, el;
455 
456 	if(activecol)
457 		c = activecol;
458 	else if(seltext && seltext->col)
459 		c = seltext->col;
460 	else if(t && t->col)
461 		c = t->col;
462 	else{
463 		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
464 			error("can't make column");
465 		c = row.col[row.ncol-1];
466 	}
467 	activecol = c;
468 	if(t==nil || t->w==nil || c->nw==0)
469 		return coladd(c, nil, nil, -1);
470 
471 	/* find biggest window and biggest blank spot */
472 	emptyw = c->w[0];
473 	bigw = emptyw;
474 	for(i=1; i<c->nw; i++){
475 		w = c->w[i];
476 		/* use >= to choose one near bottom of screen */
477 		if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
478 			bigw = w;
479 		if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
480 			emptyw = w;
481 	}
482 	emptyb = &emptyw->body;
483 	el = emptyb->fr.maxlines-emptyb->fr.nlines;
484 	/* if empty space is big, use it */
485 	if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
486 		y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
487 	else{
488 		/* if this window is in column and isn't much smaller, split it */
489 		if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
490 			bigw = t->w;
491 		y = (bigw->r.min.y + bigw->r.max.y)/2;
492 	}
493 	w = coladd(c, nil, nil, y);
494 	if(w->body.fr.maxlines < 2)
495 		colgrow(w->col, w, 1);
496 	return w;
497 }
498