Blob


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"
14 static Point prevmouse;
15 static Window *mousew;
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;
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 }
50 void
51 error(char *s)
52 {
53 fprint(2, "acme: %s: %r\n", s);
54 abort();
55 }
57 Window*
58 errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
59 {
60 Window *w;
61 Rune *r;
62 int i, n;
63 static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
65 r = runemalloc(ndir+7);
66 if(n = ndir) /* assign = */
67 runemove(r, dir, ndir);
68 runemove(r+n, Lpluserrors, 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 }
89 /* make new window, if necessary; return with it locked */
90 Window*
91 errorwin(Mntdir *md, int owner, Window *e)
92 {
93 Window *w;
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 if(w != e)
101 winlock(w, owner);
102 if(w->col != nil)
103 break;
104 /* window was deleted too fast */
105 if(w != e)
106 winunlock(w);
108 return w;
111 static void
112 printwarning(Window *ew, Mntdir *md, Rune *r)
114 int nr, q0, owner;
115 Window *w;
116 Text *t;
118 if(r == nil)
119 error("runevsmprint failed");
120 nr = runestrlen(r);
122 if(row.ncol == 0){ /* really early error */
123 rowinit(&row, screen->clipr);
124 rowadd(&row, nil, -1);
125 rowadd(&row, nil, -1);
126 if(row.ncol == 0)
127 error("initializing columns in warning()");
130 w = errorwin(md, 'E', ew);
131 t = &w->body;
132 owner = w->owner;
133 if(owner == 0)
134 w->owner = 'E';
135 wincommit(w, t);
136 q0 = textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
137 textshow(t, q0, q0+nr, 1);
138 winsettag(t->w);
139 textscrdraw(t);
140 w->owner = owner;
141 w->dirty = FALSE;
142 if(ew != w)
143 winunlock(w);
144 free(r);
147 void
148 warning(Mntdir *md, char *s, ...)
150 Rune *r;
151 va_list arg;
153 va_start(arg, s);
154 r = runevsmprint(s, arg);
155 va_end(arg);
156 printwarning(nil, md, r);
159 /*
160 * Warningew is like warning but avoids locking the error window
161 * if it's already locked by checking that ew!=error window.
162 */
163 void
164 warningew(Window *ew, Mntdir *md, char *s, ...)
166 Rune *r;
167 va_list arg;
169 va_start(arg, s);
170 r = runevsmprint(s, arg);
171 va_end(arg);
172 printwarning(ew, md, r);
175 int
176 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
178 if(n1 != n2)
179 return FALSE;
180 return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
183 uint
184 min(uint a, uint b)
186 if(a < b)
187 return a;
188 return b;
191 uint
192 max(uint a, uint b)
194 if(a > b)
195 return a;
196 return b;
199 char*
200 runetobyte(Rune *r, int n)
202 char *s;
204 if(r == nil)
205 return nil;
206 s = emalloc(n*UTFmax+1);
207 setmalloctag(s, getcallerpc(&r));
208 snprint(s, n*UTFmax+1, "%.*S", n, r);
209 return s;
212 Rune*
213 bytetorune(char *s, int *ip)
215 Rune *r;
216 int nb, nr;
218 nb = strlen(s);
219 r = runemalloc(nb+1);
220 cvttorunes(s, nb, r, &nb, &nr, nil);
221 r[nr] = '\0';
222 *ip = nr;
223 return r;
226 int
227 isalnum(Rune c)
229 /*
230 * Hard to get absolutely right. Use what we know about ASCII
231 * and assume anything above the Latin control characters is
232 * potentially an alphanumeric.
233 */
234 if(c <= ' ')
235 return FALSE;
236 if(0x7F<=c && c<=0xA0)
237 return FALSE;
238 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
239 return FALSE;
240 return TRUE;
243 int
244 rgetc(void *v, uint n)
246 return ((Rune*)v)[n];
249 int
250 tgetc(void *a, uint n)
252 Text *t;
254 t = a;
255 if(n >= t->file->b.nc)
256 return 0;
257 return textreadc(t, n);
260 Rune*
261 skipbl(Rune *r, int n, int *np)
263 while(n>0 && *r==' ' || *r=='\t' || *r=='\n'){
264 --n;
265 r++;
267 *np = n;
268 return r;
271 Rune*
272 findbl(Rune *r, int n, int *np)
274 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
275 --n;
276 r++;
278 *np = n;
279 return r;
282 void
283 savemouse(Window *w)
285 prevmouse = mouse->xy;
286 mousew = w;
289 void
290 restoremouse(Window *w)
292 if(mousew!=nil && mousew==w)
293 moveto(mousectl, prevmouse);
294 mousew = nil;
297 void
298 clearmouse()
300 mousew = nil;
303 char*
304 estrdup(char *s)
306 char *t;
308 t = strdup(s);
309 if(t == nil)
310 error("strdup failed");
311 setmalloctag(t, getcallerpc(&s));
312 return t;
315 void*
316 emalloc(uint n)
318 void *p;
320 p = malloc(n);
321 if(p == nil){
322 fprint(2, "allocating %d from %lux: %r\n", n, getcallerpc(&n));
323 *(int*)0=0;
324 error("malloc failed");
326 setmalloctag(p, getcallerpc(&n));
327 memset(p, 0, n);
328 return p;
331 void*
332 erealloc(void *p, uint n)
334 p = realloc(p, n);
335 if(p == nil){
336 fprint(2, "reallocating %d: %r\n", n);
337 error("realloc failed");
339 setmalloctag(p, getcallerpc(&n));
340 return p;
343 /*
344 * Heuristic city.
345 */
346 Window*
347 makenewwindow(Text *t)
349 Column *c;
350 Window *w, *bigw, *emptyw;
351 Text *emptyb;
352 int i, y, el;
354 if(activecol)
355 c = activecol;
356 else if(seltext && seltext->col)
357 c = seltext->col;
358 else if(t && t->col)
359 c = t->col;
360 else{
361 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
362 error("can't make column");
363 c = row.col[row.ncol-1];
365 activecol = c;
366 if(t==nil || t->w==nil || c->nw==0)
367 return coladd(c, nil, nil, -1);
369 /* find biggest window and biggest blank spot */
370 emptyw = c->w[0];
371 bigw = emptyw;
372 for(i=1; i<c->nw; i++){
373 w = c->w[i];
374 /* use >= to choose one near bottom of screen */
375 if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
376 bigw = w;
377 if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
378 emptyw = w;
380 emptyb = &emptyw->body;
381 el = emptyb->fr.maxlines-emptyb->fr.nlines;
382 /* if empty space is big, use it */
383 if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
384 y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
385 else{
386 /* if this window is in column and isn't much smaller, split it */
387 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
388 bigw = t->w;
389 y = (bigw->r.min.y + bigw->r.max.y)/2;
391 w = coladd(c, nil, nil, y);
392 if(w->body.fr.maxlines < 2)
393 colgrow(w->col, w, 1);
394 return w;