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