plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }