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