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+7); 66 if(n = ndir) /* assign = */ 67 runemove(r, dir, ndir); 68 runemove(r+n, L"+Errors", 7); 69 n += 7; 70 w = lookfile(r, n); 71 if(w == nil){ 72 if(row.ncol == 0) 73 if(rowadd(&row, nil, -1) == nil) 74 error("can't create column to make error window"); 75 w = coladd(row.col[row.ncol-1], nil, nil, -1); 76 w->filemenu = FALSE; 77 winsetname(w, r, n); 78 } 79 free(r); 80 for(i=nincl; --i>=0; ){ 81 n = runestrlen(incl[i]); 82 r = runemalloc(n); 83 runemove(r, incl[i], n); 84 winaddincl(w, r, n); 85 } 86 return w; 87 } 88 89 /* make new window, if necessary; return with it locked */ 90 Window* 91 errorwin(Mntdir *md, int owner, Window *e) 92 { 93 Window *w; 94 95 for(;;){ 96 if(md == nil) 97 w = errorwin1(nil, 0, nil, 0); 98 else 99 w = errorwin1(md->dir, md->ndir, md->incl, md->nincl); 100 if(w != e) 101 winlock(w, owner); 102 if(w->col != nil) 103 break; 104 /* window was deleted too fast */ 105 if(w != e) 106 winunlock(w); 107 } 108 return w; 109 } 110 111 static void 112 printwarning(Window *ew, Mntdir *md, Rune *r) 113 { 114 int nr, q0, owner; 115 Window *w; 116 Text *t; 117 118 if(r == nil) 119 error("runevsmprint failed"); 120 nr = runestrlen(r); 121 122 if(row.ncol == 0){ /* really early error */ 123 rowinit(&row, screen->clipr); 124 rowadd(&row, nil, -1); 125 rowadd(&row, nil, -1); 126 if(row.ncol == 0) 127 error("initializing columns in warning()"); 128 } 129 130 w = errorwin(md, 'E', ew); 131 t = &w->body; 132 owner = w->owner; 133 if(owner == 0) 134 w->owner = 'E'; 135 wincommit(w, t); 136 q0 = textbsinsert(t, t->file->nc, r, nr, TRUE, &nr); 137 textshow(t, q0, q0+nr, 1); 138 winsettag(t->w); 139 textscrdraw(t); 140 w->owner = owner; 141 w->dirty = FALSE; 142 if(ew != w) 143 winunlock(w); 144 free(r); 145 } 146 147 void 148 warning(Mntdir *md, char *s, ...) 149 { 150 Rune *r; 151 va_list arg; 152 153 va_start(arg, s); 154 r = runevsmprint(s, arg); 155 va_end(arg); 156 printwarning(nil, md, r); 157 } 158 159 /* 160 * Warningew is like warning but avoids locking the error window 161 * if it's already locked by checking that ew!=error window. 162 */ 163 void 164 warningew(Window *ew, Mntdir *md, char *s, ...) 165 { 166 Rune *r; 167 va_list arg; 168 169 va_start(arg, s); 170 r = runevsmprint(s, arg); 171 va_end(arg); 172 printwarning(ew, md, r); 173 } 174 175 int 176 runeeq(Rune *s1, uint n1, Rune *s2, uint n2) 177 { 178 if(n1 != n2) 179 return FALSE; 180 return memcmp(s1, s2, n1*sizeof(Rune)) == 0; 181 } 182 183 uint 184 min(uint a, uint b) 185 { 186 if(a < b) 187 return a; 188 return b; 189 } 190 191 uint 192 max(uint a, uint b) 193 { 194 if(a > b) 195 return a; 196 return b; 197 } 198 199 char* 200 runetobyte(Rune *r, int n) 201 { 202 char *s; 203 204 if(r == nil) 205 return nil; 206 s = emalloc(n*UTFmax+1); 207 setmalloctag(s, getcallerpc(&r)); 208 snprint(s, n*UTFmax+1, "%.*S", n, r); 209 return s; 210 } 211 212 Rune* 213 bytetorune(char *s, int *ip) 214 { 215 Rune *r; 216 int nb, nr; 217 218 nb = strlen(s); 219 r = runemalloc(nb+1); 220 cvttorunes(s, nb, r, &nb, &nr, nil); 221 r[nr] = '\0'; 222 *ip = nr; 223 return r; 224 } 225 226 int 227 isalnum(Rune c) 228 { 229 /* 230 * Hard to get absolutely right. Use what we know about ASCII 231 * and assume anything above the Latin control characters is 232 * potentially an alphanumeric. 233 */ 234 if(c <= ' ') 235 return FALSE; 236 if(0x7F<=c && c<=0xA0) 237 return FALSE; 238 if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) 239 return FALSE; 240 return TRUE; 241 } 242 243 int 244 rgetc(void *v, uint n) 245 { 246 return ((Rune*)v)[n]; 247 } 248 249 int 250 tgetc(void *a, uint n) 251 { 252 Text *t; 253 254 t = a; 255 if(n >= t->file->nc) 256 return 0; 257 return textreadc(t, n); 258 } 259 260 Rune* 261 skipbl(Rune *r, int n, int *np) 262 { 263 while(n>0 && *r==' ' || *r=='\t' || *r=='\n'){ 264 --n; 265 r++; 266 } 267 *np = n; 268 return r; 269 } 270 271 Rune* 272 findbl(Rune *r, int n, int *np) 273 { 274 while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ 275 --n; 276 r++; 277 } 278 *np = n; 279 return r; 280 } 281 282 void 283 savemouse(Window *w) 284 { 285 prevmouse = mouse->xy; 286 mousew = w; 287 } 288 289 void 290 restoremouse(Window *w) 291 { 292 if(mousew!=nil && mousew==w) 293 moveto(mousectl, prevmouse); 294 mousew = nil; 295 } 296 297 void 298 clearmouse() 299 { 300 mousew = nil; 301 } 302 303 char* 304 estrdup(char *s) 305 { 306 char *t; 307 308 t = strdup(s); 309 if(t == nil) 310 error("strdup failed"); 311 setmalloctag(t, getcallerpc(&s)); 312 return t; 313 } 314 315 void* 316 emalloc(uint n) 317 { 318 void *p; 319 320 p = malloc(n); 321 if(p == nil) 322 error("malloc failed"); 323 setmalloctag(p, getcallerpc(&n)); 324 memset(p, 0, n); 325 return p; 326 } 327 328 void* 329 erealloc(void *p, uint n) 330 { 331 p = realloc(p, n); 332 if(p == nil) 333 error("realloc failed"); 334 setmalloctag(p, getcallerpc(&n)); 335 return p; 336 } 337 338 /* 339 * Heuristic city. 340 */ 341 Window* 342 makenewwindow(Text *t) 343 { 344 Column *c; 345 Window *w, *bigw, *emptyw; 346 Text *emptyb; 347 int i, y, el; 348 349 if(activecol) 350 c = activecol; 351 else if(seltext && seltext->col) 352 c = seltext->col; 353 else if(t && t->col) 354 c = t->col; 355 else{ 356 if(row.ncol==0 && rowadd(&row, nil, -1)==nil) 357 error("can't make column"); 358 c = row.col[row.ncol-1]; 359 } 360 activecol = c; 361 if(t==nil || t->w==nil || c->nw==0) 362 return coladd(c, nil, nil, -1); 363 364 /* find biggest window and biggest blank spot */ 365 emptyw = c->w[0]; 366 bigw = emptyw; 367 for(i=1; i<c->nw; i++){ 368 w = c->w[i]; 369 /* use >= to choose one near bottom of screen */ 370 if(w->body.maxlines >= bigw->body.maxlines) 371 bigw = w; 372 if(w->body.maxlines-w->body.nlines >= emptyw->body.maxlines-emptyw->body.nlines) 373 emptyw = w; 374 } 375 emptyb = &emptyw->body; 376 el = emptyb->maxlines-emptyb->nlines; 377 /* if empty space is big, use it */ 378 if(el>15 || (el>3 && el>(bigw->body.maxlines-1)/2)) 379 y = emptyb->r.min.y+emptyb->nlines*font->height; 380 else{ 381 /* if this window is in column and isn't much smaller, split it */ 382 if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) 383 bigw = t->w; 384 y = (bigw->r.min.y + bigw->r.max.y)/2; 385 } 386 w = coladd(c, nil, nil, y); 387 if(w->body.maxlines < 2) 388 colgrow(w->col, w, 1); 389 return w; 390 } 391