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