xref: /plan9-contrib/sys/src/cmd/acme/util.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <frame.h>
8 #include <auth.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 fbufalloc(void)
52 {
53 	return emalloc(BUFSIZE);
54 }
55 
56 void
57 fbuffree(void *f)
58 {
59 	free(f);
60 }
61 
62 void
63 error(char *s)
64 {
65 	fprint(2, "acme: %s: %r\n", s);
66 	remove(acmeerrorfile);
67 	notify(nil);
68 	abort();
69 }
70 
71 Window*
72 errorwin(Rune *dir, int ndir, Rune **incl, int nincl)
73 {
74 	Window *w;
75 	Rune *r;
76 	int i, n;
77 
78 	r = runemalloc(ndir+7);
79 	if(n = ndir)	/* assign = */
80 		runemove(r, dir, ndir);
81 	runemove(r+n, L"+Errors", 7);
82 	n += 7;
83 	w = lookfile(r, n);
84 	if(w == nil){
85 		w = coladd(row.col[row.ncol-1], nil, nil, -1);
86 		w->filemenu = FALSE;
87 		winsetname(w, r, n);
88 	}
89 	free(r);
90 	for(i=nincl; --i>=0; ){
91 		n = runestrlen(incl[i]);
92 		r = runemalloc(n);
93 		runemove(r, incl[i], n);
94 		winaddincl(w, r, n);
95 	}
96 	return w;
97 }
98 
99 void
100 warning(Mntdir *md, char *s, ...)
101 {
102 	char *buf;
103 	Rune *r;
104 	int n, nb, nr, q0, owner;
105 	Window *w;
106 	Text *t;
107 	va_list arg;
108 
109 	if(row.ncol == 0){	/* really early error */
110 		rowinit(&row, screen->clipr);
111 		rowadd(&row, nil, -1);
112 		rowadd(&row, nil, -1);
113 		if(row.ncol == 0)
114 			error("initializing columns in warning()");
115 	}
116 	buf = fbufalloc();
117 	va_start(arg, s);
118 	n = doprint(buf, buf+BUFSIZE, s, arg)-buf;
119 	va_end(arg);
120 	r = runemalloc(n);
121 	cvttorunes(buf, n, r, &nb, &nr, nil);
122 	fbuffree(buf);
123 	if(md)
124 		for(;;){
125 			w = errorwin(md->dir, md->ndir, md->incl, md->nincl);
126 			winlock(w, 'E');
127 			if(w->col != nil)
128 				break;
129 			/* window was deleted too fast */
130 			winunlock(w);
131 		}
132 	else
133 		w = errorwin(nil, 0, nil, 0);
134 	t = &w->body;
135 	owner = w->owner;
136 	if(owner == 0)
137 		w->owner = 'E';
138 	wincommit(w, t);
139 	q0 = textbsinsert(t, t->file->nc, r, nr, TRUE, &nr);
140 	textshow(t, q0, q0+nr);
141 	winsettag(t->w);
142 	textscrdraw(t);
143 	w->owner = owner;
144 	w->dirty = FALSE;
145 	if(md)
146 		winunlock(w);
147 	free(r);
148 }
149 
150 int
151 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
152 {
153 	if(n1 != n2)
154 		return FALSE;
155 	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
156 }
157 
158 int
159 runestrlen(Rune *s)
160 {
161 	int i;
162 
163 	i = 0;
164 	while(*s++)
165 		i++;
166 	return i;
167 }
168 
169 Rune*
170 strrune(Rune *s, Rune c)
171 {
172 	Rune c1;
173 
174 	if(c == 0) {
175 		while(*s++)
176 			;
177 		return s-1;
178 	}
179 
180 	while(c1 = *s++)
181 		if(c1 == c)
182 			return s-1;
183 	return nil;
184 }
185 
186 uint
187 min(uint a, uint b)
188 {
189 	if(a < b)
190 		return a;
191 	return b;
192 }
193 
194 uint
195 max(uint a, uint b)
196 {
197 	if(a > b)
198 		return a;
199 	return b;
200 }
201 
202 char*
203 runetobyte(Rune *r, int n)
204 {
205 	char *s;
206 
207 	if(n == 0)
208 		return nil;
209 	s = emalloc(n*UTFmax+1);
210 	snprint(s, n*UTFmax+1, "%.*S", n, r);
211 	return s;
212 }
213 
214 Rune*
215 bytetorune(char *s, int *ip)
216 {
217 	Rune *r;
218 	int nb, nr;
219 
220 	nb = strlen(s);
221 	r = runemalloc(nb+1);
222 	cvttorunes(s, nb, r, &nb, &nr, nil);
223 	r[nr] = '\0';
224 	*ip = nr;
225 	return r;
226 }
227 
228 int
229 isalnum(Rune c)
230 {
231 	/*
232 	 * Hard to get absolutely right.  Use what we know about ASCII
233 	 * and assume anything above the Latin control characters is
234 	 * potentially an alphanumeric.
235 	 */
236 	if(c <= ' ')
237 		return FALSE;
238 	if(0x7F<=c && c<=0xA0)
239 		return FALSE;
240 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
241 		return FALSE;
242 	return TRUE;
243 }
244 
245 int
246 rgetc(void *v, uint n)
247 {
248 	return ((Rune*)v)[n];
249 }
250 
251 int
252 tgetc(void *a, uint n)
253 {
254 	Text *t;
255 
256 	t = a;
257 	if(n >= t->file->nc)
258 		return 0;
259 	return textreadc(t, n);
260 }
261 
262 Rune*
263 skipbl(Rune *r, int n, int *np)
264 {
265 	while(n>0 && *r==' ' || *r=='\t' || *r=='\n'){
266 		--n;
267 		r++;
268 	}
269 	*np = n;
270 	return r;
271 }
272 
273 Rune*
274 findbl(Rune *r, int n, int *np)
275 {
276 	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
277 		--n;
278 		r++;
279 	}
280 	*np = n;
281 	return r;
282 }
283 
284 void
285 savemouse(Window *w)
286 {
287 	prevmouse = mouse->xy;
288 	mousew = w;
289 }
290 
291 void
292 restoremouse(Window *w)
293 {
294 	if(mousew!=nil && mousew==w)
295 		moveto(mousectl, prevmouse);
296 	mousew = nil;
297 }
298 
299 void
300 clearmouse()
301 {
302 	mousew = nil;
303 }
304 
305 void*
306 emalloc(uint n)
307 {
308 	void *p;
309 
310 	p = malloc(n);
311 	if(p == nil)
312 		error("malloc failed");
313 	memset(p, 0, n);
314 	return p;
315 }
316 
317 /*
318  * Heuristic city.
319  */
320 Window*
321 newwindow(Text *t)
322 {
323 	Column *c;
324 	Window *w, *bigw, *emptyw;
325 	Text *emptyb;
326 	int i, y, el;
327 
328 	if(activecol)
329 		c = activecol;
330 	else if(seltext && seltext->col)
331 		c = seltext->col;
332 	else if(t && t->col)
333 		c = t->col;
334 	else{
335 		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
336 			error("can't make column");
337 		c = row.col[row.ncol-1];
338 	}
339 	activecol = c;
340 	if(t==nil || t->w==nil || c->nw==0)
341 		return coladd(c, nil, nil, -1);
342 
343 	/* find biggest window and biggest blank spot */
344 	emptyw = c->w[0];
345 	bigw = emptyw;
346 	for(i=1; i<c->nw; i++){
347 		w = c->w[i];
348 		/* use >= to choose one near bottom of screen */
349 		if(w->body.maxlines >= bigw->body.maxlines)
350 			bigw = w;
351 		if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
352 			emptyw = w;
353 	}
354 	emptyb = &emptyw->body;
355 	el = emptyb->maxlines-emptyb->nlines;
356 	/* if empty space is big, use it */
357 	if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
358 		y = emptyb->r.min.y+emptyb->nlines*font->height;
359 	else{
360 		/* if this window is in column and isn't much smaller, split it */
361 		if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
362 			bigw = t->w;
363 		y = (bigw->r.min.y + bigw->r.max.y)/2;
364 	}
365 	w = coladd(c, nil, nil, y);
366 	if(w->body.maxlines < 2)
367 		colgrow(w->col, w, 1);
368 	return w;
369 }
370