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