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