| #include <u.h> | 
 | #include <libc.h> | 
 | #include <draw.h> | 
 | #include <thread.h> | 
 | #include <cursor.h> | 
 | #include <mouse.h> | 
 | #include <keyboard.h> | 
 | #include <frame.h> | 
 | #include <fcall.h> | 
 | #include <plumb.h> | 
 | #include "dat.h" | 
 | #include "fns.h" | 
 |  | 
 | static	Point		prevmouse; | 
 | static	Window	*mousew; | 
 |  | 
 | Range | 
 | range(int q0, int q1) | 
 | { | 
 | 	Range r; | 
 |  | 
 | 	r.q0 = q0; | 
 | 	r.q1 = q1; | 
 | 	return r; | 
 | } | 
 |  | 
 | Runestr | 
 | runestr(Rune *r, uint n) | 
 | { | 
 | 	Runestr rs; | 
 |  | 
 | 	rs.r = r; | 
 | 	rs.nr = n; | 
 | 	return rs; | 
 | } | 
 |  | 
 | void | 
 | cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls) | 
 | { | 
 | 	uchar *q; | 
 | 	Rune *s; | 
 | 	int j, w; | 
 |  | 
 | 	/* | 
 | 	 * Always guaranteed that n bytes may be interpreted | 
 | 	 * without worrying about partial runes.  This may mean | 
 | 	 * reading up to UTFmax-1 more bytes than n; the caller | 
 | 	 * knows this.  If n is a firm limit, the caller should | 
 | 	 * set p[n] = 0. | 
 | 	 */ | 
 | 	q = (uchar*)p; | 
 | 	s = r; | 
 | 	for(j=0; j<n; j+=w){ | 
 | 		if(*q < Runeself){ | 
 | 			w = 1; | 
 | 			*s = *q++; | 
 | 		}else{ | 
 | 			w = chartorune(s, (char*)q); | 
 | 			q += w; | 
 | 		} | 
 | 		if(*s) | 
 | 			s++; | 
 | 		else if(nulls) | 
 | 			*nulls = TRUE; | 
 | 	} | 
 | 	*nb = (char*)q-p; | 
 | 	*nr = s-r; | 
 | } | 
 |  | 
 | void | 
 | error(char *s) | 
 | { | 
 | 	fprint(2, "acme: %s: %r\n", s); | 
 | 	threadexitsall(nil); | 
 | } | 
 |  | 
 | Window* | 
 | errorwin1(Rune *dir, int ndir, Rune **incl, int nincl) | 
 | { | 
 | 	Window *w; | 
 | 	Rune *r; | 
 | 	int i, n; | 
 | 	static Rune Lpluserrors[] = { '+', 'E', 'r', 'r', 'o', 'r', 's', 0 }; | 
 |  | 
 | 	r = runemalloc(ndir+8); | 
 | 	if((n = ndir) != 0){ | 
 | 		runemove(r, dir, ndir); | 
 | 		r[n++] = L'/'; | 
 | 	} | 
 | 	runemove(r+n, Lpluserrors, 7); | 
 | 	n += 7; | 
 | 	w = lookfile(r, n); | 
 | 	if(w == nil){ | 
 | 		if(row.ncol == 0) | 
 | 			if(rowadd(&row, nil, -1) == nil) | 
 | 				error("can't create column to make error window"); | 
 | 		w = coladd(row.col[row.ncol-1], nil, nil, -1); | 
 | 		w->filemenu = FALSE; | 
 | 		winsetname(w, r, n); | 
 | 	} | 
 | 	free(r); | 
 | 	for(i=nincl; --i>=0; ){ | 
 | 		n = runestrlen(incl[i]); | 
 | 		r = runemalloc(n); | 
 | 		runemove(r, incl[i], n); | 
 | 		winaddincl(w, r, n); | 
 | 	} | 
 | 	w->autoindent = globalautoindent; | 
 | 	return w; | 
 | } | 
 |  | 
 | /* make new window, if necessary; return with it locked */ | 
 | Window* | 
 | errorwin(Mntdir *md, int owner) | 
 | { | 
 | 	Window *w; | 
 |  | 
 | 	for(;;){ | 
 | 		if(md == nil) | 
 | 			w = errorwin1(nil, 0, nil, 0); | 
 | 		else | 
 | 			w = errorwin1(md->dir, md->ndir, md->incl, md->nincl); | 
 | 		winlock(w, owner); | 
 | 		if(w->col != nil) | 
 | 			break; | 
 | 		/* window was deleted too fast */ | 
 | 		winunlock(w); | 
 | 	} | 
 | 	return w; | 
 | } | 
 |  | 
 | /* | 
 |  * Incoming window should be locked.  | 
 |  * It will be unlocked and returned window | 
 |  * will be locked in its place. | 
 |  */ | 
 | Window* | 
 | errorwinforwin(Window *w) | 
 | { | 
 | 	int i, n, nincl, owner; | 
 | 	Rune **incl; | 
 | 	Runestr dir; | 
 | 	Text *t; | 
 |  | 
 | 	t = &w->body; | 
 | 	dir = dirname(t, nil, 0); | 
 | 	if(dir.nr==1 && dir.r[0]=='.'){	/* sigh */ | 
 | 		free(dir.r); | 
 | 		dir.r = nil; | 
 | 		dir.nr = 0; | 
 | 	} | 
 | 	incl = nil; | 
 | 	nincl = w->nincl; | 
 | 	if(nincl > 0){ | 
 | 		incl = emalloc(nincl*sizeof(Rune*)); | 
 | 		for(i=0; i<nincl; i++){ | 
 | 			n = runestrlen(w->incl[i]); | 
 | 			incl[i] = runemalloc(n+1); | 
 | 			runemove(incl[i], w->incl[i], n); | 
 | 		} | 
 | 	} | 
 | 	owner = w->owner; | 
 | 	winunlock(w); | 
 | 	for(;;){ | 
 | 		w = errorwin1(dir.r, dir.nr, incl, nincl); | 
 | 		winlock(w, owner); | 
 | 		if(w->col != nil) | 
 | 			break; | 
 | 		/* window deleted too fast */ | 
 | 		winunlock(w); | 
 | 	} | 
 | 	return w; | 
 | } | 
 |  | 
 | typedef struct Warning Warning; | 
 |  | 
 | struct Warning{ | 
 | 	Mntdir *md; | 
 | 	Buffer buf; | 
 | 	Warning *next; | 
 | }; | 
 |  | 
 | static Warning *warnings; | 
 |  | 
 | static | 
 | void | 
 | addwarningtext(Mntdir *md, Rune *r, int nr) | 
 | { | 
 | 	Warning *warn; | 
 | 	 | 
 | 	for(warn = warnings; warn; warn=warn->next){ | 
 | 		if(warn->md == md){ | 
 | 			bufinsert(&warn->buf, warn->buf.nc, r, nr); | 
 | 			return; | 
 | 		} | 
 | 	} | 
 | 	warn = emalloc(sizeof(Warning)); | 
 | 	warn->next = warnings; | 
 | 	warn->md = md; | 
 | 	if(md) | 
 | 		fsysincid(md); | 
 | 	warnings = warn; | 
 | 	bufinsert(&warn->buf, 0, r, nr); | 
 | 	nbsendp(cwarn, 0); | 
 | } | 
 |  | 
 | /* called while row is locked */ | 
 | void | 
 | flushwarnings(void) | 
 | { | 
 | 	Warning *warn, *next; | 
 | 	Window *w; | 
 | 	Text *t; | 
 | 	int owner, nr, q0, n; | 
 | 	Rune *r; | 
 |  | 
 | 	for(warn=warnings; warn; warn=next) { | 
 | 		w = errorwin(warn->md, 'E'); | 
 | 		t = &w->body; | 
 | 		owner = w->owner; | 
 | 		if(owner == 0) | 
 | 			w->owner = 'E'; | 
 | 		wincommit(w, t); | 
 | 		/* | 
 | 		 * Most commands don't generate much output. For instance, | 
 | 		 * Edit ,>cat goes through /dev/cons and is already in blocks | 
 | 		 * because of the i/o system, but a few can.  Edit ,p will | 
 | 		 * put the entire result into a single hunk.  So it's worth doing | 
 | 		 * this in blocks (and putting the text in a buffer in the first | 
 | 		 * place), to avoid a big memory footprint. | 
 | 		 */ | 
 | 		r = fbufalloc(); | 
 | 		q0 = t->file->b.nc; | 
 | 		for(n = 0; n < warn->buf.nc; n += nr){ | 
 | 			nr = warn->buf.nc - n; | 
 | 			if(nr > RBUFSIZE) | 
 | 				nr = RBUFSIZE; | 
 | 			bufread(&warn->buf, n, r, nr); | 
 | 			textbsinsert(t, t->file->b.nc, r, nr, TRUE, &nr); | 
 | 		} | 
 | 		textshow(t, q0, t->file->b.nc, 1); | 
 | 		free(r); | 
 | 		winsettag(t->w); | 
 | 		textscrdraw(t); | 
 | 		w->owner = owner; | 
 | 		w->dirty = FALSE; | 
 | 		winunlock(w); | 
 | 		bufclose(&warn->buf); | 
 | 		next = warn->next; | 
 | 		if(warn->md) | 
 | 			fsysdelid(warn->md); | 
 | 		free(warn); | 
 | 	} | 
 | 	warnings = nil; | 
 | } | 
 |  | 
 | void | 
 | warning(Mntdir *md, char *s, ...) | 
 | { | 
 | 	Rune *r; | 
 | 	va_list arg; | 
 |  | 
 | 	va_start(arg, s); | 
 | 	r = runevsmprint(s, arg); | 
 | 	va_end(arg); | 
 | 	if(r == nil) | 
 | 		error("runevsmprint failed"); | 
 | 	addwarningtext(md, r, runestrlen(r)); | 
 | 	free(r); | 
 | } | 
 |  | 
 | int | 
 | runeeq(Rune *s1, uint n1, Rune *s2, uint n2) | 
 | { | 
 | 	if(n1 != n2) | 
 | 		return FALSE; | 
 | 	return memcmp(s1, s2, n1*sizeof(Rune)) == 0; | 
 | } | 
 |  | 
 | uint | 
 | min(uint a, uint b) | 
 | { | 
 | 	if(a < b) | 
 | 		return a; | 
 | 	return b; | 
 | } | 
 |  | 
 | uint | 
 | max(uint a, uint b) | 
 | { | 
 | 	if(a > b) | 
 | 		return a; | 
 | 	return b; | 
 | } | 
 |  | 
 | char* | 
 | runetobyte(Rune *r, int n) | 
 | { | 
 | 	char *s; | 
 |  | 
 | 	if(r == nil) | 
 | 		return nil; | 
 | 	s = emalloc(n*UTFmax+1); | 
 | 	setmalloctag(s, getcallerpc(&r)); | 
 | 	snprint(s, n*UTFmax+1, "%.*S", n, r); | 
 | 	return s; | 
 | } | 
 |  | 
 | Rune* | 
 | bytetorune(char *s, int *ip) | 
 | { | 
 | 	Rune *r; | 
 | 	int nb, nr; | 
 |  | 
 | 	nb = strlen(s); | 
 | 	r = runemalloc(nb+1); | 
 | 	cvttorunes(s, nb, r, &nb, &nr, nil); | 
 | 	r[nr] = '\0'; | 
 | 	*ip = nr; | 
 | 	return r; | 
 | } | 
 |  | 
 | int | 
 | isalnum(Rune c) | 
 | { | 
 | 	/* | 
 | 	 * Hard to get absolutely right.  Use what we know about ASCII | 
 | 	 * and assume anything above the Latin control characters is | 
 | 	 * potentially an alphanumeric. | 
 | 	 */ | 
 | 	if(c <= ' ') | 
 | 		return FALSE; | 
 | 	if(0x7F<=c && c<=0xA0) | 
 | 		return FALSE; | 
 | 	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c)) | 
 | 		return FALSE; | 
 | 	return TRUE; | 
 | } | 
 |  | 
 | int | 
 | rgetc(void *v, uint n) | 
 | { | 
 | 	return ((Rune*)v)[n]; | 
 | } | 
 |  | 
 | int | 
 | tgetc(void *a, uint n) | 
 | { | 
 | 	Text *t; | 
 |  | 
 | 	t = a; | 
 | 	if(n >= t->file->b.nc) | 
 | 		return 0; | 
 | 	return textreadc(t, n); | 
 | } | 
 |  | 
 | Rune* | 
 | skipbl(Rune *r, int n, int *np) | 
 | { | 
 | 	while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){ | 
 | 		--n; | 
 | 		r++; | 
 | 	} | 
 | 	*np = n; | 
 | 	return r; | 
 | } | 
 |  | 
 | Rune* | 
 | findbl(Rune *r, int n, int *np) | 
 | { | 
 | 	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){ | 
 | 		--n; | 
 | 		r++; | 
 | 	} | 
 | 	*np = n; | 
 | 	return r; | 
 | } | 
 |  | 
 | void | 
 | savemouse(Window *w) | 
 | { | 
 | 	prevmouse = mouse->xy; | 
 | 	mousew = w; | 
 | } | 
 |  | 
 | int | 
 | restoremouse(Window *w) | 
 | { | 
 | 	int did; | 
 |  | 
 | 	did = 0; | 
 | 	if(mousew!=nil && mousew==w) { | 
 | 		moveto(mousectl, prevmouse); | 
 | 		did = 1; | 
 | 	} | 
 | 	mousew = nil; | 
 | 	return did; | 
 | } | 
 |  | 
 | void | 
 | clearmouse() | 
 | { | 
 | 	mousew = nil; | 
 | } | 
 |  | 
 | char* | 
 | estrdup(char *s) | 
 | { | 
 | 	char *t; | 
 |  | 
 | 	t = strdup(s); | 
 | 	if(t == nil) | 
 | 		error("strdup failed"); | 
 | 	setmalloctag(t, getcallerpc(&s)); | 
 | 	return t; | 
 | } | 
 |  | 
 | void* | 
 | emalloc(uint n) | 
 | { | 
 | 	void *p; | 
 |  | 
 | 	p = malloc(n); | 
 | 	if(p == nil) | 
 | 		error("malloc failed"); | 
 | 	setmalloctag(p, getcallerpc(&n)); | 
 | 	memset(p, 0, n); | 
 | 	return p; | 
 | } | 
 |  | 
 | void* | 
 | erealloc(void *p, uint n) | 
 | { | 
 | 	p = realloc(p, n); | 
 | 	if(p == nil) | 
 | 		error("realloc failed"); | 
 | 	setmalloctag(p, getcallerpc(&n)); | 
 | 	return p; | 
 | } | 
 |  | 
 | /* | 
 |  * Heuristic city. | 
 |  */ | 
 | Window* | 
 | makenewwindow(Text *t) | 
 | { | 
 | 	Column *c; | 
 | 	Window *w, *bigw, *emptyw; | 
 | 	Text *emptyb; | 
 | 	int i, y, el; | 
 |  | 
 | 	if(activecol) | 
 | 		c = activecol; | 
 | 	else if(seltext && seltext->col) | 
 | 		c = seltext->col; | 
 | 	else if(t && t->col) | 
 | 		c = t->col; | 
 | 	else{ | 
 | 		if(row.ncol==0 && rowadd(&row, nil, -1)==nil) | 
 | 			error("can't make column"); | 
 | 		c = row.col[row.ncol-1]; | 
 | 	} | 
 | 	activecol = c; | 
 | 	if(t==nil || t->w==nil || c->nw==0) | 
 | 		return coladd(c, nil, nil, -1); | 
 |  | 
 | 	/* find biggest window and biggest blank spot */ | 
 | 	emptyw = c->w[0]; | 
 | 	bigw = emptyw; | 
 | 	for(i=1; i<c->nw; i++){ | 
 | 		w = c->w[i]; | 
 | 		/* use >= to choose one near bottom of screen */ | 
 | 		if(w->body.fr.maxlines >= bigw->body.fr.maxlines) | 
 | 			bigw = w; | 
 | 		if(w->body.fr.maxlines-w->body.fr.nlines >= emptyw->body.fr.maxlines-emptyw->body.fr.nlines) | 
 | 			emptyw = w; | 
 | 	} | 
 | 	emptyb = &emptyw->body; | 
 | 	el = emptyb->fr.maxlines-emptyb->fr.nlines; | 
 | 	/* if empty space is big, use it */ | 
 | 	if(el>15 || (el>3 && el>(bigw->body.fr.maxlines-1)/2)) | 
 | 		y = emptyb->fr.r.min.y+emptyb->fr.nlines*font->height; | 
 | 	else{ | 
 | 		/* if this window is in column and isn't much smaller, split it */ | 
 | 		if(t->col==c && Dy(t->w->r)>2*Dy(bigw->r)/3) | 
 | 			bigw = t->w; | 
 | 		y = (bigw->r.min.y + bigw->r.max.y)/2; | 
 | 	} | 
 | 	w = coladd(c, nil, nil, y); | 
 | 	if(w->body.fr.maxlines < 2) | 
 | 		colgrow(w->col, w, 1); | 
 | 	return w; | 
 | } |