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
cvttorunes(char * p,int n,Rune * r,int * nb,int * nr,int * nulls)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
error(char * s)51 error(char *s)
52 {
53 fprint(2, "acme: %s: %r\n", s);
54 remove(acmeerrorfile);
55 abort();
56 }
57
58 Window*
errorwin1(Rune * dir,int ndir,Rune ** incl,int nincl)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*
errorwin(Mntdir * md,int owner)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*
errorwinforwin(Window * w)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
addwarningtext(Mntdir * md,Rune * r,int nr)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
flushwarnings(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
warning(Mntdir * md,char * s,...)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
runeeq(Rune * s1,uint n1,Rune * s2,uint n2)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
min(uint a,uint b)261 min(uint a, uint b)
262 {
263 if(a < b)
264 return a;
265 return b;
266 }
267
268 uint
max(uint a,uint b)269 max(uint a, uint b)
270 {
271 if(a > b)
272 return a;
273 return b;
274 }
275
276 char*
runetobyte(Rune * r,int n)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*
bytetorune(char * s,int * ip)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
isalnum(Rune c)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
rgetc(void * v,uint n)324 rgetc(void *v, uint n)
325 {
326 return ((Rune*)v)[n];
327 }
328
329 int
tgetc(void * a,uint n)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*
skipbl(Rune * r,int n,int * np)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*
findbl(Rune * r,int n,int * np)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
savemouse(Window * w)363 savemouse(Window *w)
364 {
365 prevmouse = mouse->xy;
366 mousew = w;
367 }
368
369 int
restoremouse(Window * w)370 restoremouse(Window *w)
371 {
372 int did;
373
374 did = 0;
375 if(mousew!=nil && mousew==w){
376 moveto(mousectl, prevmouse);
377 did = 1;
378 }
379 mousew = nil;
380 return did;
381 }
382
383 void
clearmouse()384 clearmouse()
385 {
386 mousew = nil;
387 }
388
389 char*
estrdup(char * s)390 estrdup(char *s)
391 {
392 char *t;
393
394 t = strdup(s);
395 if(t == nil)
396 error("strdup failed");
397 setmalloctag(t, getcallerpc(&s));
398 return t;
399 }
400
401 void*
emalloc(uint n)402 emalloc(uint n)
403 {
404 void *p;
405
406 p = malloc(n);
407 if(p == nil)
408 error("malloc failed");
409 setmalloctag(p, getcallerpc(&n));
410 memset(p, 0, n);
411 return p;
412 }
413
414 void*
erealloc(void * p,uint n)415 erealloc(void *p, uint n)
416 {
417 p = realloc(p, n);
418 if(p == nil)
419 error("realloc failed");
420 setmalloctag(p, getcallerpc(&n));
421 return p;
422 }
423
424 /*
425 * Heuristic city.
426 */
427 Window*
makenewwindow(Text * t)428 makenewwindow(Text *t)
429 {
430 Column *c;
431 Window *w, *bigw, *emptyw;
432 Text *emptyb;
433 int i, y, el;
434
435 if(activecol)
436 c = activecol;
437 else if(seltext && seltext->col)
438 c = seltext->col;
439 else if(t && t->col)
440 c = t->col;
441 else{
442 if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
443 error("can't make column");
444 c = row.col[row.ncol-1];
445 }
446 activecol = c;
447 if(t==nil || t->w==nil || c->nw==0)
448 return coladd(c, nil, nil, -1);
449
450 /* find biggest window and biggest blank spot */
451 emptyw = c->w[0];
452 bigw = emptyw;
453 for(i=1; i<c->nw; i++){
454 w = c->w[i];
455 /* use >= to choose one near bottom of screen */
456 if(w->body.maxlines >= bigw->body.maxlines)
457 bigw = w;
458 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines)
459 emptyw = w;
460 }
461 emptyb = &emptyw->body;
462 el = emptyb->maxlines-emptyb->nlines;
463 /* if empty space is big, use it */
464 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2))
465 y = emptyb->r.min.y+emptyb->nlines*font->height;
466 else{
467 /* if this window is in column and isn't much smaller, split it */
468 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3)
469 bigw = t->w;
470 y = (bigw->r.min.y + bigw->r.max.y)/2;
471 }
472 w = coladd(c, nil, nil, y);
473 if(w->body.maxlines < 2)
474 colgrow(w->col, w, 1);
475 return w;
476 }
477