xref: /plan9-contrib/sys/src/cmd/acme/util.c (revision 9b943567965ba040fd275927fbe088656eb8ce4f)
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
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
51 error(char *s)
52 {
53 	fprint(2, "acme: %s: %r\n", s);
54 	remove(acmeerrorfile);
55 	abort();
56 }
57 
58 Window*
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*
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 typedef struct Warning Warning;
113 
114 struct Warning{
115 	Mntdir *md;
116 	Buffer buf;
117 	Warning *next;
118 };
119 
120 static Warning *warnings;
121 
122 static
123 void
124 addwarningtext(Mntdir *md, Rune *r, int nr)
125 {
126 	Warning *warn;
127 
128 	for(warn = warnings; warn; warn=warn->next){
129 		if(warn->md == md){
130 			bufinsert(&warn->buf, warn->buf.nc, r, nr);
131 			return;
132 		}
133 	}
134 	warn = emalloc(sizeof(Warning));
135 	warn->next = warnings;
136 	warnings = warn;
137 	bufinsert(&warn->buf, 0, r, nr);
138 }
139 
140 /* called while row is locked */
141 void
142 flushwarnings(void)
143 {
144 	Warning *warn, *next;
145 	Window *w;
146 	Text *t;
147 	int owner, nr, q0, n;
148 	Rune *r;
149 
150 	if(row.ncol == 0){	/* really early error */
151 		rowinit(&row, screen->clipr);
152 		rowadd(&row, nil, -1);
153 		rowadd(&row, nil, -1);
154 		if(row.ncol == 0)
155 			error("initializing columns in flushwarnings()");
156 	}
157 
158 	for(warn=warnings; warn; warn=next) {
159 		w = errorwin(warn->md, 'E');
160 		t = &w->body;
161 		owner = w->owner;
162 		if(owner == 0)
163 			w->owner = 'E';
164 		wincommit(w, t);
165 		/*
166 		 * Most commands don't generate much output. For instance,
167 		 * Edit ,>cat goes through /dev/cons and is already in blocks
168 		 * because of the i/o system, but a few can.  Edit ,p will
169 		 * put the entire result into a single hunk.  So it's worth doing
170 		 * this in blocks (and putting the text in a buffer in the first
171 		 * place), to avoid a big memory footprint.
172 		 */
173 		r = fbufalloc();
174 		q0 = t->file->nc;
175 		for(n = 0; n < warn->buf.nc; n += nr){
176 			nr = warn->buf.nc - n;
177 			if(nr > RBUFSIZE)
178 				nr = RBUFSIZE;
179 			bufread(&warn->buf, n, r, nr);
180 			textbsinsert(t, t->file->nc, r, nr, TRUE, &nr);
181 		}
182 		textshow(t, q0, t->file->nc, 1);
183 		free(r);
184 		winsettag(t->w);
185 		textscrdraw(t);
186 		w->owner = owner;
187 		w->dirty = FALSE;
188 		winunlock(w);
189 		bufclose(&warn->buf);
190 		next = warn->next;
191 		free(warn);
192 	}
193 	warnings = nil;
194 }
195 
196 void
197 warning(Mntdir *md, char *s, ...)
198 {
199 	Rune *r;
200 	va_list arg;
201 
202 	va_start(arg, s);
203 	r = runevsmprint(s, arg);
204 	va_end(arg);
205 	if(r == nil)
206 		error("runevsmprint failed");
207 	addwarningtext(md, r, runestrlen(r));
208 }
209 
210 int
211 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
212 {
213 	if(n1 != n2)
214 		return FALSE;
215 	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
216 }
217 
218 uint
219 min(uint a, uint b)
220 {
221 	if(a < b)
222 		return a;
223 	return b;
224 }
225 
226 uint
227 max(uint a, uint b)
228 {
229 	if(a > b)
230 		return a;
231 	return b;
232 }
233 
234 char*
235 runetobyte(Rune *r, int n)
236 {
237 	char *s;
238 
239 	if(r == nil)
240 		return nil;
241 	s = emalloc(n*UTFmax+1);
242 	setmalloctag(s, getcallerpc(&r));
243 	snprint(s, n*UTFmax+1, "%.*S", n, r);
244 	return s;
245 }
246 
247 Rune*
248 bytetorune(char *s, int *ip)
249 {
250 	Rune *r;
251 	int nb, nr;
252 
253 	nb = strlen(s);
254 	r = runemalloc(nb+1);
255 	cvttorunes(s, nb, r, &nb, &nr, nil);
256 	r[nr] = '\0';
257 	*ip = nr;
258 	return r;
259 }
260 
261 int
262 isalnum(Rune c)
263 {
264 	/*
265 	 * Hard to get absolutely right.  Use what we know about ASCII
266 	 * and assume anything above the Latin control characters is
267 	 * potentially an alphanumeric.
268 	 */
269 	if(c <= ' ')
270 		return FALSE;
271 	if(0x7F<=c && c<=0xA0)
272 		return FALSE;
273 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
274 		return FALSE;
275 	return TRUE;
276 }
277 
278 int
279 rgetc(void *v, uint n)
280 {
281 	return ((Rune*)v)[n];
282 }
283 
284 int
285 tgetc(void *a, uint n)
286 {
287 	Text *t;
288 
289 	t = a;
290 	if(n >= t->file->nc)
291 		return 0;
292 	return textreadc(t, n);
293 }
294 
295 Rune*
296 skipbl(Rune *r, int n, int *np)
297 {
298 	while(n>0 && *r==' ' || *r=='\t' || *r=='\n'){
299 		--n;
300 		r++;
301 	}
302 	*np = n;
303 	return r;
304 }
305 
306 Rune*
307 findbl(Rune *r, int n, int *np)
308 {
309 	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
310 		--n;
311 		r++;
312 	}
313 	*np = n;
314 	return r;
315 }
316 
317 void
318 savemouse(Window *w)
319 {
320 	prevmouse = mouse->xy;
321 	mousew = w;
322 }
323 
324 void
325 restoremouse(Window *w)
326 {
327 	if(mousew!=nil && mousew==w)
328 		moveto(mousectl, prevmouse);
329 	mousew = nil;
330 }
331 
332 void
333 clearmouse()
334 {
335 	mousew = nil;
336 }
337 
338 char*
339 estrdup(char *s)
340 {
341 	char *t;
342 
343 	t = strdup(s);
344 	if(t == nil)
345 		error("strdup failed");
346 	setmalloctag(t, getcallerpc(&s));
347 	return t;
348 }
349 
350 void*
351 emalloc(uint n)
352 {
353 	void *p;
354 
355 	p = malloc(n);
356 	if(p == nil)
357 		error("malloc failed");
358 	setmalloctag(p, getcallerpc(&n));
359 	memset(p, 0, n);
360 	return p;
361 }
362 
363 void*
364 erealloc(void *p, uint n)
365 {
366 	p = realloc(p, n);
367 	if(p == nil)
368 		error("realloc failed");
369 	setmalloctag(p, getcallerpc(&n));
370 	return p;
371 }
372 
373 /*
374  * Heuristic city.
375  */
376 Window*
377 makenewwindow(Text *t)
378 {
379 	Column *c;
380 	Window *w, *bigw, *emptyw;
381 	Text *emptyb;
382 	int i, y, el;
383 
384 	if(activecol)
385 		c = activecol;
386 	else if(seltext && seltext->col)
387 		c = seltext->col;
388 	else if(t && t->col)
389 		c = t->col;
390 	else{
391 		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
392 			error("can't make column");
393 		c = row.col[row.ncol-1];
394 	}
395 	activecol = c;
396 	if(t==nil || t->w==nil || c->nw==0)
397 		return coladd(c, nil, nil, -1);
398 
399 	/* find biggest window and biggest blank spot */
400 	emptyw = c->w[0];
401 	bigw = emptyw;
402 	for(i=1; i<c->nw; i++){
403 		w = c->w[i];
404 		/* use >= to choose one near bottom of screen */
405 		if(w->body.maxlines >= bigw->body.maxlines)
406 			bigw = w;
407 		if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
408 			emptyw = w;
409 	}
410 	emptyb = &emptyw->body;
411 	el = emptyb->maxlines-emptyb->nlines;
412 	/* if empty space is big, use it */
413 	if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
414 		y = emptyb->r.min.y+emptyb->nlines*font->height;
415 	else{
416 		/* if this window is in column and isn't much smaller, split it */
417 		if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
418 			bigw = t->w;
419 		y = (bigw->r.min.y + bigw->r.max.y)/2;
420 	}
421 	w = coladd(c, nil, nil, y);
422 	if(w->body.maxlines < 2)
423 		colgrow(w->col, w, 1);
424 	return w;
425 }
426