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 free(r); 250 } 251 252 int 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 261 min(uint a, uint b) 262 { 263 if(a < b) 264 return a; 265 return b; 266 } 267 268 uint 269 max(uint a, uint b) 270 { 271 if(a > b) 272 return a; 273 return b; 274 } 275 276 char* 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* 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 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 if(c <= ' ') 312 return FALSE; 313 if(0x7F<=c && c<=0xA0) 314 return FALSE; 315 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) 316 return FALSE; 317 return TRUE; 318 } 319 320 int 321 rgetc(void *v, uint n) 322 { 323 return ((Rune*)v)[n]; 324 } 325 326 int 327 tgetc(void *a, uint n) 328 { 329 Text *t; 330 331 t = a; 332 if(n >= t->file->nc) 333 return 0; 334 return textreadc(t, n); 335 } 336 337 Rune* 338 skipbl(Rune *r, int n, int *np) 339 { 340 while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ 341 --n; 342 r++; 343 } 344 *np = n; 345 return r; 346 } 347 348 Rune* 349 findbl(Rune *r, int n, int *np) 350 { 351 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ 352 --n; 353 r++; 354 } 355 *np = n; 356 return r; 357 } 358 359 void 360 savemouse(Window *w) 361 { 362 prevmouse = mouse->xy; 363 mousew = w; 364 } 365 366 void 367 restoremouse(Window *w) 368 { 369 if(mousew!=nil && mousew==w) 370 moveto(mousectl, prevmouse); 371 mousew = nil; 372 } 373 374 void 375 clearmouse() 376 { 377 mousew = nil; 378 } 379 380 char* 381 estrdup(char *s) 382 { 383 char *t; 384 385 t = strdup(s); 386 if(t == nil) 387 error("strdup failed"); 388 setmalloctag(t, getcallerpc(&s)); 389 return t; 390 } 391 392 void* 393 emalloc(uint n) 394 { 395 void *p; 396 397 p = malloc(n); 398 if(p == nil) 399 error("malloc failed"); 400 setmalloctag(p, getcallerpc(&n)); 401 memset(p, 0, n); 402 return p; 403 } 404 405 void* 406 erealloc(void *p, uint n) 407 { 408 p = realloc(p, n); 409 if(p == nil) 410 error("realloc failed"); 411 setmalloctag(p, getcallerpc(&n)); 412 return p; 413 } 414 415 /* 416 * Heuristic city. 417 */ 418 Window* 419 makenewwindow(Text *t) 420 { 421 Column *c; 422 Window *w, *bigw, *emptyw; 423 Text *emptyb; 424 int i, y, el; 425 426 if(activecol) 427 c = activecol; 428 else if(seltext && seltext->col) 429 c = seltext->col; 430 else if(t && t->col) 431 c = t->col; 432 else{ 433 if(row.ncol==0 && rowadd(&row, nil, -1)==nil) 434 error("can't make column"); 435 c = row.col[row.ncol-1]; 436 } 437 activecol = c; 438 if(t==nil || t->w==nil || c->nw==0) 439 return coladd(c, nil, nil, -1); 440 441 /* find biggest window and biggest blank spot */ 442 emptyw = c->w[0]; 443 bigw = emptyw; 444 for(i=1; i<c->nw; i++){ 445 w = c->w[i]; 446 /* use >= to choose one near bottom of screen */ 447 if(w->body.maxlines >= bigw->body.maxlines) 448 bigw = w; 449 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) 450 emptyw = w; 451 } 452 emptyb = &emptyw->body; 453 el = emptyb->maxlines-emptyb->nlines; 454 /* if empty space is big, use it */ 455 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) 456 y = emptyb->r.min.y+emptyb->nlines*font->height; 457 else{ 458 /* if this window is in column and isn't much smaller, split it */ 459 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) 460 bigw = t->w; 461 y = (bigw->r.min.y + bigw->r.max.y)/2; 462 } 463 w = coladd(c, nil, nil, y); 464 if(w->body.maxlines < 2) 465 colgrow(w->col, w, 1); 466 return w; 467 } 468