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