util.c (8372B)
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 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 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 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 72 error(char *s) 73 { 74 fprint(2, "acme: %s: %r\n", s); 75 threadexitsall(nil); 76 } 77 78 Window* 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* 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* 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 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 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 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 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 285 min(uint a, uint b) 286 { 287 if(a < b) 288 return a; 289 return b; 290 } 291 292 uint 293 max(uint a, uint b) 294 { 295 if(a > b) 296 return a; 297 return b; 298 } 299 300 char* 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* 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 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 345 rgetc(void *v, uint n) 346 { 347 return ((Rune*)v)[n]; 348 } 349 350 int 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* 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* 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 384 savemouse(Window *w) 385 { 386 prevmouse = mouse->xy; 387 mousew = w; 388 } 389 390 int 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 405 clearmouse() 406 { 407 mousew = nil; 408 } 409 410 char* 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* 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* 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* 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 }