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 Range
18 range(int q0, int q1)
19 {
20 Range r;
22 r.q0 = q0;
23 r.q1 = q1;
24 return r;
25 }
27 Runestr
28 runestr(Rune *r, uint n)
29 {
30 Runestr rs;
32 rs.r = r;
33 rs.nr = n;
34 return rs;
35 }
37 void
38 cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
39 {
40 uchar *q;
41 Rune *s;
42 int j, w;
44 /*
45 * Always guaranteed that n bytes may be interpreted
46 * without worrying about partial runes. This may mean
47 * reading up to UTFmax-1 more bytes than n; the caller
48 * knows this. If n is a firm limit, the caller should
49 * set p[n] = 0.
50 */
51 q = (uchar*)p;
52 s = r;
53 for(j=0; j<n; j+=w){
54 if(*q < Runeself){
55 w = 1;
56 *s = *q++;
57 }else{
58 w = chartorune(s, (char*)q);
59 q += w;
60 }
61 if(*s)
62 s++;
63 else if(nulls)
64 *nulls = TRUE;
65 }
66 *nb = (char*)q-p;
67 *nr = s-r;
68 }
70 void
71 error(char *s)
72 {
73 fprint(2, "acme: %s: %r\n", s);
74 threadexitsall(nil);
75 }
77 Window*
78 errorwin1(Rune *dir, int ndir, Rune **incl, int nincl)
79 {
80 Window *w;
81 Rune *r;
82 int i, n;
83 static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 };
85 r = runemalloc(ndir+8);
86 if((n = ndir) != 0){
87 runemove(r, dir, ndir);
88 r[n++] = L'/';
89 }
90 runemove(r+n, Lpluserrors, 7);
91 n += 7;
92 w = lookfile(r, n);
93 if(w == nil){
94 if(row.ncol == 0)
95 if(rowadd(&row, nil, -1) == nil)
96 error("can't create column to make error window");
97 w = coladd(row.col[row.ncol-1], nil, nil, -1);
98 w->filemenu = FALSE;
99 winsetname(w, r, n);
101 free(r);
102 for(i=nincl; --i>=0; ){
103 n = runestrlen(incl[i]);
104 r = runemalloc(n);
105 runemove(r, incl[i], n);
106 winaddincl(w, r, n);
108 w->autoindent = globalautoindent;
109 return w;
112 /* make new window, if necessary; return with it locked */
113 Window*
114 errorwin(Mntdir *md, int owner)
116 Window *w;
118 for(;;){
119 if(md == nil)
120 w = errorwin1(nil, 0, nil, 0);
121 else
122 w = errorwin1(md->dir, md->ndir, md->incl, md->nincl);
123 winlock(w, owner);
124 if(w->col != nil)
125 break;
126 /* window was deleted too fast */
127 winunlock(w);
129 return w;
132 /*
133 * Incoming window should be locked.
134 * It will be unlocked and returned window
135 * will be locked in its place.
136 */
137 Window*
138 errorwinforwin(Window *w)
140 int i, n, nincl, owner;
141 Rune **incl;
142 Runestr dir;
143 Text *t;
145 t = &w->body;
146 dir = dirname(t, nil, 0);
147 if(dir.nr==1 && dir.r[0]=='.'){ /* sigh */
148 free(dir.r);
149 dir.r = nil;
150 dir.nr = 0;
152 incl = nil;
153 nincl = w->nincl;
154 if(nincl > 0){
155 incl = emalloc(nincl*sizeof(Rune*));
156 for(i=0; i<nincl; i++){
157 n = runestrlen(w->incl[i]);
158 incl[i] = runemalloc(n+1);
159 runemove(incl[i], w->incl[i], n);
162 owner = w->owner;
163 winunlock(w);
164 for(;;){
165 w = errorwin1(dir.r, dir.nr, incl, nincl);
166 winlock(w, owner);
167 if(w->col != nil)
168 break;
169 /* window deleted too fast */
170 winunlock(w);
172 return w;
175 typedef struct Warning Warning;
177 struct Warning{
178 Mntdir *md;
179 Buffer buf;
180 Warning *next;
181 };
183 static Warning *warnings;
185 static
186 void
187 addwarningtext(Mntdir *md, Rune *r, int nr)
189 Warning *warn;
191 for(warn = warnings; warn; warn=warn->next){
192 if(warn->md == md){
193 bufinsert(&warn->buf, warn->buf.nc, r, nr);
194 return;
197 warn = emalloc(sizeof(Warning));
198 warn->next = warnings;
199 warn->md = md;
200 if(md)
201 fsysincid(md);
202 warnings = warn;
203 bufinsert(&warn->buf, 0, r, nr);
204 nbsendp(cwarn, 0);
207 /* called while row is locked */
208 void
209 flushwarnings(void)
211 Warning *warn, *next;
212 Window *w;
213 Text *t;
214 int owner, nr, q0, n;
215 Rune *r;
217 for(warn=warnings; warn; warn=next) {
218 w = errorwin(warn->md, 'E');
219 t = &w->body;
220 owner = w->owner;
221 if(owner == 0)
222 w->owner = 'E';
223 wincommit(w, t);
224 /*
225 * Most commands don't generate much output. For instance,
226 * Edit ,>cat goes through /dev/cons and is already in blocks
227 * because of the i/o system, but a few can. Edit ,p will
228 * put the entire result into a single hunk. So it's worth doing
229 * this in blocks (and putting the text in a buffer in the first
230 * place), to avoid a big memory footprint.
231 */
232 r = fbufalloc();
233 q0 = t->file->b.nc;
234 for(n = 0; n < warn->buf.nc; n += nr){
235 nr = warn->buf.nc - n;
236 if(nr > RBUFSIZE)
237 nr = RBUFSIZE;
238 bufread(&warn->buf, n, r, nr);
239 textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr);
241 textshow(t, q0, t->file->b.nc, 1);
242 free(r);
243 winsettag(t->w);
244 textscrdraw(t);
245 w->owner = owner;
246 w->dirty = FALSE;
247 winunlock(w);
248 bufclose(&warn->buf);
249 next = warn->next;
250 if(warn->md)
251 fsysdelid(warn->md);
252 free(warn);
254 warnings = nil;
257 void
258 warning(Mntdir *md, char *s, ...)
260 Rune *r;
261 va_list arg;
263 va_start(arg, s);
264 r = runevsmprint(s, arg);
265 va_end(arg);
266 if(r == nil)
267 error("runevsmprint failed");
268 addwarningtext(md, r, runestrlen(r));
271 int
272 runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
274 if(n1 != n2)
275 return FALSE;
276 return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
279 uint
280 min(uint a, uint b)
282 if(a < b)
283 return a;
284 return b;
287 uint
288 max(uint a, uint b)
290 if(a > b)
291 return a;
292 return b;
295 char*
296 runetobyte(Rune *r, int n)
298 char *s;
300 if(r == nil)
301 return nil;
302 s = emalloc(n*UTFmax+1);
303 setmalloctag(s, getcallerpc(&r));
304 snprint(s, n*UTFmax+1, "%.*S", n, r);
305 return s;
308 Rune*
309 bytetorune(char *s, int *ip)
311 Rune *r;
312 int nb, nr;
314 nb = strlen(s);
315 r = runemalloc(nb+1);
316 cvttorunes(s, nb, r, &nb, &nr, nil);
317 r[nr] = '\0';
318 *ip = nr;
319 return r;
322 int
323 isalnum(Rune c)
325 /*
326 * Hard to get absolutely right. Use what we know about ASCII
327 * and assume anything above the Latin control characters is
328 * potentially an alphanumeric.
329 */
330 if(c <= ' ')
331 return FALSE;
332 if(0x7F<=c && c<=0xA0)
333 return FALSE;
334 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
335 return FALSE;
336 return TRUE;
339 int
340 rgetc(void *v, uint n)
342 return ((Rune*)v)[n];
345 int
346 tgetc(void *a, uint n)
348 Text *t;
350 t = a;
351 if(n >= t->file->b.nc)
352 return 0;
353 return textreadc(t, n);
356 Rune*
357 skipbl(Rune *r, int n, int *np)
359 while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
360 --n;
361 r++;
363 *np = n;
364 return r;
367 Rune*
368 findbl(Rune *r, int n, int *np)
370 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
371 --n;
372 r++;
374 *np = n;
375 return r;
378 void
379 savemouse(Window *w)
381 prevmouse = mouse->xy;
382 mousew = w;
385 void
386 restoremouse(Window *w)
388 if(mousew!=nil && mousew==w)
389 moveto(mousectl, prevmouse);
390 mousew = nil;
393 void
394 clearmouse()
396 mousew = nil;
399 char*
400 estrdup(char *s)
402 char *t;
404 t = strdup(s);
405 if(t == nil)
406 error("strdup failed");
407 setmalloctag(t, getcallerpc(&s));
408 return t;
411 void*
412 emalloc(uint n)
414 void *p;
416 p = malloc(n);
417 if(p == nil){
418 fprint(2, "allocating %d from %lux: %r\n", n, getcallerpc(&n));
419 *(int*)0=0;
420 error("malloc failed");
422 setmalloctag(p, getcallerpc(&n));
423 memset(p, 0, n);
424 return p;
427 void*
428 erealloc(void *p, uint n)
430 p = realloc(p, n);
431 if(p == nil){
432 fprint(2, "reallocating %d: %r\n", n);
433 error("realloc failed");
435 setmalloctag(p, getcallerpc(&n));
436 return p;
439 /*
440 * Heuristic city.
441 */
442 Window*
443 makenewwindow(Text *t)
445 Column *c;
446 Window *w, *bigw, *emptyw;
447 Text *emptyb;
448 int i, y, el;
450 if(activecol)
451 c = activecol;
452 else if(seltext && seltext->col)
453 c = seltext->col;
454 else if(t && t->col)
455 c = t->col;
456 else{
457 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
458 error("can't make column");
459 c = row.col[row.ncol-1];
461 activecol = c;
462 if(t==nil || t->w==nil || c->nw==0)
463 return coladd(c, nil, nil, -1);
465 /* find biggest window and biggest blank spot */
466 emptyw = c->w[0];
467 bigw = emptyw;
468 for(i=1; i<c->nw; i++){
469 w = c->w[i];
470 /* use >= to choose one near bottom of screen */
471 if(w->body.fr.maxlines >= bigw->body.fr.maxlines)
472 bigw = w;
473 if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines)
474 emptyw = w;
476 emptyb = &emptyw->body;
477 el = emptyb->fr.maxlines-emptyb->fr.nlines;
478 /* if empty space is big, use it */
479 if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2))
480 y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height;
481 else{
482 /* if this window is in column and isn't much smaller, split it */
483 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
484 bigw = t->w;
485 y = (bigw->r.min.y + bigw->r.max.y)/2;
487 w = coladd(c, nil, nil, y);
488 if(w->body.fr.maxlines < 2)
489 colgrow(w->col, w, 1);
490 return w;