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 * 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 324 rgetc(void *v, uint n) 325 { 326 return ((Rune*)v)[n]; 327 } 328 329 int 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* 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* 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 363 savemouse(Window *w) 364 { 365 prevmouse = mouse->xy; 366 mousew = w; 367 } 368 369 void 370 restoremouse(Window *w) 371 { 372 if(mousew!=nil && mousew==w) 373 moveto(mousectl, prevmouse); 374 mousew = nil; 375 } 376 377 void 378 clearmouse() 379 { 380 mousew = nil; 381 } 382 383 char* 384 estrdup(char *s) 385 { 386 char *t; 387 388 t = strdup(s); 389 if(t == nil) 390 error("strdup failed"); 391 setmalloctag(t, getcallerpc(&s)); 392 return t; 393 } 394 395 void* 396 emalloc(uint n) 397 { 398 void *p; 399 400 p = malloc(n); 401 if(p == nil) 402 error("malloc failed"); 403 setmalloctag(p, getcallerpc(&n)); 404 memset(p, 0, n); 405 return p; 406 } 407 408 void* 409 erealloc(void *p, uint n) 410 { 411 p = realloc(p, n); 412 if(p == nil) 413 error("realloc failed"); 414 setmalloctag(p, getcallerpc(&n)); 415 return p; 416 } 417 418 /* 419 * Heuristic city. 420 */ 421 Window* 422 makenewwindow(Text *t) 423 { 424 Column *c; 425 Window *w, *bigw, *emptyw; 426 Text *emptyb; 427 int i, y, el; 428 429 if(activecol) 430 c = activecol; 431 else if(seltext && seltext->col) 432 c = seltext->col; 433 else if(t && t->col) 434 c = t->col; 435 else{ 436 if(row.ncol==0 && rowadd(&row, nil, -1)==nil) 437 error("can't make column"); 438 c = row.col[row.ncol-1]; 439 } 440 activecol = c; 441 if(t==nil || t->w==nil || c->nw==0) 442 return coladd(c, nil, nil, -1); 443 444 /* find biggest window and biggest blank spot */ 445 emptyw = c->w[0]; 446 bigw = emptyw; 447 for(i=1; i<c->nw; i++){ 448 w = c->w[i]; 449 /* use >= to choose one near bottom of screen */ 450 if(w->body.maxlines >= bigw->body.maxlines) 451 bigw = w; 452 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) 453 emptyw = w; 454 } 455 emptyb = &emptyw->body; 456 el = emptyb->maxlines-emptyb->nlines; 457 /* if empty space is big, use it */ 458 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) 459 y = emptyb->r.min.y+emptyb->nlines*font->height; 460 else{ 461 /* if this window is in column and isn't much smaller, split it */ 462 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) 463 bigw = t->w; 464 y = (bigw->r.min.y + bigw->r.max.y)/2; 465 } 466 w = coladd(c, nil, nil, y); 467 if(w->body.maxlines < 2) 468 colgrow(w->col, w, 1); 469 return w; 470 } 471