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 } 250 251 int 252 runeeq(Rune *s1, uint n1, Rune *s2, uint n2) 253 { 254 if(n1 != n2) 255 return FALSE; 256 return memcmp(s1, s2, n1*sizeof(Rune)) == 0; 257 } 258 259 uint 260 min(uint a, uint b) 261 { 262 if(a < b) 263 return a; 264 return b; 265 } 266 267 uint 268 max(uint a, uint b) 269 { 270 if(a > b) 271 return a; 272 return b; 273 } 274 275 char* 276 runetobyte(Rune *r, int n) 277 { 278 char *s; 279 280 if(r == nil) 281 return nil; 282 s = emalloc(n*UTFmax+1); 283 setmalloctag(s, getcallerpc(&r)); 284 snprint(s, n*UTFmax+1, "%.*S", n, r); 285 return s; 286 } 287 288 Rune* 289 bytetorune(char *s, int *ip) 290 { 291 Rune *r; 292 int nb, nr; 293 294 nb = strlen(s); 295 r = runemalloc(nb+1); 296 cvttorunes(s, nb, r, &nb, &nr, nil); 297 r[nr] = '\0'; 298 *ip = nr; 299 return r; 300 } 301 302 int 303 isalnum(Rune c) 304 { 305 /* 306 * Hard to get absolutely right. Use what we know about ASCII 307 * and assume anything above the Latin control characters is 308 * potentially an alphanumeric. 309 */ 310 if(c <= ' ') 311 return FALSE; 312 if(0x7F<=c && c<=0xA0) 313 return FALSE; 314 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) 315 return FALSE; 316 return TRUE; 317 } 318 319 int 320 rgetc(void *v, uint n) 321 { 322 return ((Rune*)v)[n]; 323 } 324 325 int 326 tgetc(void *a, uint n) 327 { 328 Text *t; 329 330 t = a; 331 if(n >= t->file->nc) 332 return 0; 333 return textreadc(t, n); 334 } 335 336 Rune* 337 skipbl(Rune *r, int n, int *np) 338 { 339 while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ 340 --n; 341 r++; 342 } 343 *np = n; 344 return r; 345 } 346 347 Rune* 348 findbl(Rune *r, int n, int *np) 349 { 350 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ 351 --n; 352 r++; 353 } 354 *np = n; 355 return r; 356 } 357 358 void 359 savemouse(Window *w) 360 { 361 prevmouse = mouse->xy; 362 mousew = w; 363 } 364 365 void 366 restoremouse(Window *w) 367 { 368 if(mousew!=nil && mousew==w) 369 moveto(mousectl, prevmouse); 370 mousew = nil; 371 } 372 373 void 374 clearmouse() 375 { 376 mousew = nil; 377 } 378 379 char* 380 estrdup(char *s) 381 { 382 char *t; 383 384 t = strdup(s); 385 if(t == nil) 386 error("strdup failed"); 387 setmalloctag(t, getcallerpc(&s)); 388 return t; 389 } 390 391 void* 392 emalloc(uint n) 393 { 394 void *p; 395 396 p = malloc(n); 397 if(p == nil) 398 error("malloc failed"); 399 setmalloctag(p, getcallerpc(&n)); 400 memset(p, 0, n); 401 return p; 402 } 403 404 void* 405 erealloc(void *p, uint n) 406 { 407 p = realloc(p, n); 408 if(p == nil) 409 error("realloc failed"); 410 setmalloctag(p, getcallerpc(&n)); 411 return p; 412 } 413 414 /* 415 * Heuristic city. 416 */ 417 Window* 418 makenewwindow(Text *t) 419 { 420 Column *c; 421 Window *w, *bigw, *emptyw; 422 Text *emptyb; 423 int i, y, el; 424 425 if(activecol) 426 c = activecol; 427 else if(seltext && seltext->col) 428 c = seltext->col; 429 else if(t && t->col) 430 c = t->col; 431 else{ 432 if(row.ncol==0 && rowadd(&row, nil, -1)==nil) 433 error("can't make column"); 434 c = row.col[row.ncol-1]; 435 } 436 activecol = c; 437 if(t==nil || t->w==nil || c->nw==0) 438 return coladd(c, nil, nil, -1); 439 440 /* find biggest window and biggest blank spot */ 441 emptyw = c->w[0]; 442 bigw = emptyw; 443 for(i=1; i<c->nw; i++){ 444 w = c->w[i]; 445 /* use >= to choose one near bottom of screen */ 446 if(w->body.maxlines >= bigw->body.maxlines) 447 bigw = w; 448 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) 449 emptyw = w; 450 } 451 emptyb = &emptyw->body; 452 el = emptyb->maxlines-emptyb->nlines; 453 /* if empty space is big, use it */ 454 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) 455 y = emptyb->r.min.y+emptyb->nlines*font->height; 456 else{ 457 /* if this window is in column and isn't much smaller, split it */ 458 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) 459 bigw = t->w; 460 y = (bigw->r.min.y + bigw->r.max.y)/2; 461 } 462 w = coladd(c, nil, nil, y); 463 if(w->body.maxlines < 2) 464 colgrow(w->col, w, 1); 465 return w; 466 } 467