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 typedef struct Warning Warning; 113 114 struct Warning{ 115 Mntdir *md; 116 Buffer buf; 117 Warning *next; 118 }; 119 120 static Warning *warnings; 121 122 static 123 void 124 addwarningtext(Mntdir *md, Rune *r, int nr) 125 { 126 Warning *warn; 127 128 for(warn = warnings; warn; warn=warn->next){ 129 if(warn->md == md){ 130 bufinsert(&warn->buf, warn->buf.nc, r, nr); 131 return; 132 } 133 } 134 warn = emalloc(sizeof(Warning)); 135 warn->next = warnings; 136 warn->md = md; 137 if(md) 138 fsysincid(md); 139 warnings = warn; 140 bufinsert(&warn->buf, 0, r, nr); 141 nbsendp(cwarn, 0); 142 } 143 144 /* called while row is locked */ 145 void 146 flushwarnings(void) 147 { 148 Warning *warn, *next; 149 Window *w; 150 Text *t; 151 int owner, nr, q0, n; 152 Rune *r; 153 154 if(row.ncol == 0){ /* really early error */ 155 rowinit(&row, screen->clipr); 156 rowadd(&row, nil, -1); 157 rowadd(&row, nil, -1); 158 if(row.ncol == 0) 159 error("initializing columns in flushwarnings()"); 160 } 161 162 for(warn=warnings; warn; warn=next) { 163 w = errorwin(warn->md, 'E'); 164 t = &w->body; 165 owner = w->owner; 166 if(owner == 0) 167 w->owner = 'E'; 168 wincommit(w, t); 169 /* 170 * Most commands don't generate much output. For instance, 171 * Edit ,>cat goes through /dev/cons and is already in blocks 172 * because of the i/o system, but a few can. Edit ,p will 173 * put the entire result into a single hunk. So it's worth doing 174 * this in blocks (and putting the text in a buffer in the first 175 * place), to avoid a big memory footprint. 176 */ 177 r = fbufalloc(); 178 q0 = t->file->nc; 179 for(n = 0; n < warn->buf.nc; n += nr){ 180 nr = warn->buf.nc - n; 181 if(nr > RBUFSIZE) 182 nr = RBUFSIZE; 183 bufread(&warn->buf, n, r, nr); 184 textbsinsert(t, t->file->nc, r, nr, TRUE, &nr); 185 } 186 textshow(t, q0, t->file->nc, 1); 187 free(r); 188 winsettag(t->w); 189 textscrdraw(t); 190 w->owner = owner; 191 w->dirty = FALSE; 192 winunlock(w); 193 bufclose(&warn->buf); 194 next = warn->next; 195 if(warn->md) 196 fsysdelid(warn->md); 197 free(warn); 198 } 199 warnings = nil; 200 } 201 202 void 203 warning(Mntdir *md, char *s, ...) 204 { 205 Rune *r; 206 va_list arg; 207 208 va_start(arg, s); 209 r = runevsmprint(s, arg); 210 va_end(arg); 211 if(r == nil) 212 error("runevsmprint failed"); 213 addwarningtext(md, r, runestrlen(r)); 214 } 215 216 int 217 runeeq(Rune *s1, uint n1, Rune *s2, uint n2) 218 { 219 if(n1 != n2) 220 return FALSE; 221 return memcmp(s1, s2, n1*sizeof(Rune)) == 0; 222 } 223 224 uint 225 min(uint a, uint b) 226 { 227 if(a < b) 228 return a; 229 return b; 230 } 231 232 uint 233 max(uint a, uint b) 234 { 235 if(a > b) 236 return a; 237 return b; 238 } 239 240 char* 241 runetobyte(Rune *r, int n) 242 { 243 char *s; 244 245 if(r == nil) 246 return nil; 247 s = emalloc(n*UTFmax+1); 248 setmalloctag(s, getcallerpc(&r)); 249 snprint(s, n*UTFmax+1, "%.*S", n, r); 250 return s; 251 } 252 253 Rune* 254 bytetorune(char *s, int *ip) 255 { 256 Rune *r; 257 int nb, nr; 258 259 nb = strlen(s); 260 r = runemalloc(nb+1); 261 cvttorunes(s, nb, r, &nb, &nr, nil); 262 r[nr] = '\0'; 263 *ip = nr; 264 return r; 265 } 266 267 int 268 isalnum(Rune c) 269 { 270 /* 271 * Hard to get absolutely right. Use what we know about ASCII 272 * and assume anything above the Latin control characters is 273 * potentially an alphanumeric. 274 */ 275 if(c <= ' ') 276 return FALSE; 277 if(0x7F<=c && c<=0xA0) 278 return FALSE; 279 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) 280 return FALSE; 281 return TRUE; 282 } 283 284 int 285 rgetc(void *v, uint n) 286 { 287 return ((Rune*)v)[n]; 288 } 289 290 int 291 tgetc(void *a, uint n) 292 { 293 Text *t; 294 295 t = a; 296 if(n >= t->file->nc) 297 return 0; 298 return textreadc(t, n); 299 } 300 301 Rune* 302 skipbl(Rune *r, int n, int *np) 303 { 304 while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ 305 --n; 306 r++; 307 } 308 *np = n; 309 return r; 310 } 311 312 Rune* 313 findbl(Rune *r, int n, int *np) 314 { 315 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ 316 --n; 317 r++; 318 } 319 *np = n; 320 return r; 321 } 322 323 void 324 savemouse(Window *w) 325 { 326 prevmouse = mouse->xy; 327 mousew = w; 328 } 329 330 void 331 restoremouse(Window *w) 332 { 333 if(mousew!=nil && mousew==w) 334 moveto(mousectl, prevmouse); 335 mousew = nil; 336 } 337 338 void 339 clearmouse() 340 { 341 mousew = nil; 342 } 343 344 char* 345 estrdup(char *s) 346 { 347 char *t; 348 349 t = strdup(s); 350 if(t == nil) 351 error("strdup failed"); 352 setmalloctag(t, getcallerpc(&s)); 353 return t; 354 } 355 356 void* 357 emalloc(uint n) 358 { 359 void *p; 360 361 p = malloc(n); 362 if(p == nil) 363 error("malloc failed"); 364 setmalloctag(p, getcallerpc(&n)); 365 memset(p, 0, n); 366 return p; 367 } 368 369 void* 370 erealloc(void *p, uint n) 371 { 372 p = realloc(p, n); 373 if(p == nil) 374 error("realloc failed"); 375 setmalloctag(p, getcallerpc(&n)); 376 return p; 377 } 378 379 /* 380 * Heuristic city. 381 */ 382 Window* 383 makenewwindow(Text *t) 384 { 385 Column *c; 386 Window *w, *bigw, *emptyw; 387 Text *emptyb; 388 int i, y, el; 389 390 if(activecol) 391 c = activecol; 392 else if(seltext && seltext->col) 393 c = seltext->col; 394 else if(t && t->col) 395 c = t->col; 396 else{ 397 if(row.ncol==0 && rowadd(&row, nil, -1)==nil) 398 error("can't make column"); 399 c = row.col[row.ncol-1]; 400 } 401 activecol = c; 402 if(t==nil || t->w==nil || c->nw==0) 403 return coladd(c, nil, nil, -1); 404 405 /* find biggest window and biggest blank spot */ 406 emptyw = c->w[0]; 407 bigw = emptyw; 408 for(i=1; i<c->nw; i++){ 409 w = c->w[i]; 410 /* use >= to choose one near bottom of screen */ 411 if(w->body.maxlines >= bigw->body.maxlines) 412 bigw = w; 413 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) 414 emptyw = w; 415 } 416 emptyb = &emptyw->body; 417 el = emptyb->maxlines-emptyb->nlines; 418 /* if empty space is big, use it */ 419 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) 420 y = emptyb->r.min.y+emptyb->nlines*font->height; 421 else{ 422 /* if this window is in column and isn't much smaller, split it */ 423 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) 424 bigw = t->w; 425 y = (bigw->r.min.y + bigw->r.max.y)/2; 426 } 427 w = coladd(c, nil, nil, y); 428 if(w->body.maxlines < 2) 429 colgrow(w->col, w, 1); 430 return w; 431 } 432