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