]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/acme/util.c
stats: show amount of reclaimable pages (add -r flag)
[plan9front.git] / sys / src / cmd / acme / util.c
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+8);
66         if(n = ndir){   /* assign = */
67                 runemove(r, dir, ndir);
68                 r[n++] = L'/';
69         }
70         runemove(r+n, L"+Errors", 7);
71         n += 7;
72         w = lookfile(r, n);
73         if(w == nil){
74                 if(row.ncol == 0)
75                         if(rowadd(&row, nil, -1) == nil)
76                                 error("can't create column to make error window");
77                 w = coladd(row.col[row.ncol-1], nil, nil, -1);
78                 w->filemenu = FALSE;
79                 winsetname(w, r, n);
80         }
81         free(r);
82         for(i=nincl; --i>=0; ){
83                 n = runestrlen(incl[i]);
84                 r = runemalloc(n);
85                 runemove(r, incl[i], n);
86                 winaddincl(w, r, n);
87         }
88         w->autoindent = globalautoindent;
89         return w;
90 }
91
92 /* make new window, if necessary; return with it locked */
93 Window*
94 errorwin(Mntdir *md, int owner)
95 {
96         Window *w;
97
98         for(;;){
99                 if(md == nil)
100                         w = errorwin1(nil, 0, nil, 0);
101                 else
102                         w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
103                 winlock(w, owner);
104                 if(w->col != nil)
105                         break;
106                 /* window was deleted too fast */
107                 winunlock(w);
108         }
109         return w;
110 }
111
112 /*
113  * Incoming window should be locked. 
114  * It will be unlocked and returned window
115  * will be locked in its place.
116  */
117 Window*
118 errorwinforwin(Window *w)
119 {
120         int i, n, nincl, owner;
121         Rune **incl;
122         Runestr dir;
123         Text *t;
124
125         t = &w->body;
126         dir = dirname(t, nil, 0);
127         if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
128                 free(dir.r);
129                 dir.r = nil;
130                 dir.nr = 0;
131         }
132         incl = nil;
133         nincl = w->nincl;
134         if(nincl > 0){
135                 incl = emalloc(nincl*sizeof(Rune*));
136                 for(i=0; i<nincl; i++){
137                         n = runestrlen(w->incl[i]);
138                         incl[i] = runemalloc(n+1);
139                         runemove(incl[i], w->incl[i], n);
140                 }
141         }
142         owner = w->owner;
143         winunlock(w);
144         for(;;){
145                 w = errorwin1(dir.r, dir.nr, incl, nincl);
146                 winlock(w, owner);
147                 if(w->col != nil)
148                         break;
149                 /* window deleted too fast */
150                 winunlock(w);
151         }
152         return w;
153 }
154
155 typedef struct Warning Warning;
156
157 struct Warning{
158         Mntdir *md;
159         Buffer buf;
160         Warning *next;
161 };
162
163 static Warning *warnings;
164
165 static
166 void
167 addwarningtext(Mntdir *md, Rune *r, int nr)
168 {
169         Warning *warn;
170         
171         for(warn = warnings; warn; warn=warn->next){
172                 if(warn->md == md){
173                         bufinsert(&warn->buf, warn->buf.nc, r, nr);
174                         return;
175                 }
176         }
177         warn = emalloc(sizeof(Warning));
178         warn->next = warnings;
179         warn->md = md;
180         if(md)
181                 fsysincid(md);
182         warnings = warn;
183         bufinsert(&warn->buf, 0, r, nr);
184         nbsendp(cwarn, 0);
185 }
186
187 /* called while row is locked */
188 void
189 flushwarnings(void)
190 {
191         Warning *warn, *next;
192         Window *w;
193         Text *t;
194         int owner, nr, q0, n;
195         Rune *r;
196
197         for(warn=warnings; warn; warn=next) {
198                 w = errorwin(warn->md, 'E');
199                 t = &w->body;
200                 owner = w->owner;
201                 if(owner == 0)
202                         w->owner = 'E';
203                 wincommit(w, t);
204                 /*
205                  * Most commands don't generate much output. For instance,
206                  * Edit ,>cat goes through /dev/cons and is already in blocks
207                  * because of the i/o system, but a few can.  Edit ,p will
208                  * put the entire result into a single hunk.  So it's worth doing
209                  * this in blocks (and putting the text in a buffer in the first
210                  * place), to avoid a big memory footprint.
211                  */
212                 r = fbufalloc();
213                 q0 = t->file->nc;
214                 for(n = 0; n < warn->buf.nc; n += nr){
215                         nr = warn->buf.nc - n;
216                         if(nr > RBUFSIZE)
217                                 nr = RBUFSIZE;
218                         bufread(&warn->buf, n, r, nr);
219                         textbsinsert(t, t->file->nc, r, nr, TRUE, &nr);
220                 }
221                 textshow(t, q0, t->file->nc, 1);
222                 free(r);
223                 winsettag(t->w);
224                 textscrdraw(t);
225                 w->owner = owner;
226                 w->dirty = FALSE;
227                 winunlock(w);
228                 bufclose(&warn->buf);
229                 next = warn->next;
230                 if(warn->md)
231                         fsysdelid(warn->md);
232                 free(warn);
233         }
234         warnings = nil;
235 }
236
237 void
238 warning(Mntdir *md, char *s, ...)
239 {
240         Rune *r;
241         va_list arg;
242
243         va_start(arg, s);
244         r = runevsmprint(s, arg);
245         va_end(arg);
246         if(r == nil)
247                 error("runevsmprint failed");
248         addwarningtext(md, r, runestrlen(r));
249         free(r);
250 }
251
252 int
253 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
254 {
255         if(n1 != n2)
256                 return FALSE;
257         return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
258 }
259
260 uint
261 min(uint a, uint b)
262 {
263         if(a < b)
264                 return a;
265         return b;
266 }
267
268 uint
269 max(uint a, uint b)
270 {
271         if(a > b)
272                 return a;
273         return b;
274 }
275
276 char*
277 runetobyte(Rune *r, int n)
278 {
279         char *s;
280
281         if(r == nil)
282                 return nil;
283         s = emalloc(n*UTFmax+1);
284         setmalloctag(s, getcallerpc(&r));
285         snprint(s, n*UTFmax+1, "%.*S", n, r);
286         return s;
287 }
288
289 Rune*
290 bytetorune(char *s, int *ip)
291 {
292         Rune *r;
293         int nb, nr;
294
295         nb = strlen(s);
296         r = runemalloc(nb+1);
297         cvttorunes(s, nb, r, &nb, &nr, nil);
298         r[nr] = '\0';
299         *ip = nr;
300         return r;
301 }
302
303 int
304 isalnum(Rune c)
305 {
306         /*
307          * Hard to get absolutely right.  Use what we know about ASCII
308          * and assume anything above the Latin control characters is
309          * potentially an alphanumeric.
310          *
311          * Treat 0xA0 (non-breaking space) as a special alphanumeric
312          * character [sape]
313          */
314         if(c <= ' ')
315                 return FALSE;
316         if(0x7F<=c && c<0xA0)
317                 return FALSE;
318         if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
319                 return FALSE;
320         return TRUE;
321 }
322
323 int
324 rgetc(void *v, uint n)
325 {
326         return ((Rune*)v)[n];
327 }
328
329 int
330 tgetc(void *a, uint n)
331 {
332         Text *t;
333
334         t = a;
335         if(n >= t->file->nc)
336                 return 0;
337         return textreadc(t, n);
338 }
339
340 Rune*
341 skipbl(Rune *r, int n, int *np)
342 {
343         while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
344                 --n;
345                 r++;
346         }
347         *np = n;
348         return r;
349 }
350
351 Rune*
352 findbl(Rune *r, int n, int *np)
353 {
354         while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
355                 --n;
356                 r++;
357         }
358         *np = n;
359         return r;
360 }
361
362 void
363 savemouse(Window *w)
364 {
365         prevmouse = mouse->xy;
366         mousew = w;
367 }
368
369 void
370 restoremouse(Window *w)
371 {
372         if(mousew!=nil && mousew==w)
373                 moveto(mousectl, prevmouse);
374         mousew = nil;
375 }
376
377 void
378 clearmouse()
379 {
380         mousew = nil;
381 }
382
383 char*
384 estrdup(char *s)
385 {
386         char *t;
387
388         t = strdup(s);
389         if(t == nil)
390                 error("strdup failed");
391         setmalloctag(t, getcallerpc(&s));
392         return t;
393 }
394
395 void*
396 emalloc(uint n)
397 {
398         void *p;
399
400         p = malloc(n);
401         if(p == nil)
402                 error("malloc failed");
403         setmalloctag(p, getcallerpc(&n));
404         memset(p, 0, n);
405         return p;
406 }
407
408 void*
409 erealloc(void *p, uint n)
410 {
411         p = realloc(p, n);
412         if(p == nil)
413                 error("realloc failed");
414         setmalloctag(p, getcallerpc(&n));
415         return p;
416 }
417
418 /*
419  * Heuristic city.
420  */
421 Window*
422 makenewwindow(Text *t)
423 {
424         Column *c;
425         Window *w, *bigw, *emptyw;
426         Text *emptyb;
427         int i, y, el;
428
429         if(activecol)
430                 c = activecol;
431         else if(seltext && seltext->col)
432                 c = seltext->col;
433         else if(t && t->col)
434                 c = t->col;
435         else{
436                 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
437                         error("can't make column");
438                 c = row.col[row.ncol-1];
439         }
440         activecol = c;
441         if(t==nil || t->w==nil || c->nw==0)
442                 return coladd(c, nil, nil, -1);
443
444         /* find biggest window and biggest blank spot */
445         emptyw = c->w[0];
446         bigw = emptyw;
447         for(i=1; i<c->nw; i++){
448                 w = c->w[i];
449                 /* use >= to choose one near bottom of screen */
450                 if(w->body.maxlines >= bigw->body.maxlines)
451                         bigw = w;
452                 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
453                         emptyw = w;
454         }
455         emptyb = &emptyw->body;
456         el = emptyb->maxlines-emptyb->nlines;
457         /* if empty space is big, use it */
458         if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
459                 y = emptyb->r.min.y+emptyb->nlines*font->height;
460         else{
461                 /* if this window is in column and isn't much smaller, split it */
462                 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
463                         bigw = t->w;
464                 y = (bigw->r.min.y + bigw->r.max.y)/2;
465         }
466         w = coladd(c, nil, nil, y);
467         if(w->body.maxlines < 2)
468                 colgrow(w->col, w, 1);
469         return w;
470 }