⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 text.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
#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 <complete.h>#include "dat.h"#include "fns.h"Image	*tagcols[NCOL];Image	*textcols[NCOL];enum{	TABDIR = 3	/* width of tabs in directory windows */};voidtextinit(Text *t, File *f, Rectangle r, Reffont *rf, Image *cols[NCOL]){	t->file = f;	t->all = r;	t->scrollr = r;	t->scrollr.max.x = r.min.x+Scrollwid;	t->lastsr = nullrect;	r.min.x += Scrollwid+Scrollgap;	t->eq0 = ~0;	t->ncache = 0;	t->reffont = rf;	t->tabstop = maxtab;	memmove(t->Frame.cols, cols, sizeof t->Frame.cols);	textredraw(t, r, rf->f, screen, -1);}voidtextredraw(Text *t, Rectangle r, Font *f, Image *b, int odx){	int maxt;	Rectangle rr;	frinit(t, r, f, b, t->Frame.cols);	rr = t->r;	rr.min.x -= Scrollwid+Scrollgap;	/* back fill to scroll bar */	draw(t->b, rr, t->cols[BACK], nil, ZP);	/* use no wider than 3-space tabs in a directory */	maxt = maxtab;	if(t->what == Body){		if(t->w->isdir)			maxt = min(TABDIR, maxtab);		else			maxt = t->tabstop;	}	t->maxtab = maxt*stringwidth(f, "0");	if(t->what==Body && t->w->isdir && odx!=Dx(t->all)){		if(t->maxlines > 0){			textreset(t);			textcolumnate(t, t->w->dlp,  t->w->ndl);			textshow(t, 0, 0, 1);		}	}else{		textfill(t);		textsetselect(t, t->q0, t->q1);	}}inttextresize(Text *t, Rectangle r){	int odx;	if(Dy(r) > 0)		r.max.y -= Dy(r)%t->font->height;	else		r.max.y = r.min.y;	odx = Dx(t->all);	t->all = r;	t->scrollr = r;	t->scrollr.max.x = r.min.x+Scrollwid;	t->lastsr = nullrect;	r.min.x += Scrollwid+Scrollgap;	frclear(t, 0);	textredraw(t, r, t->font, t->b, odx);	return r.max.y;}voidtextclose(Text *t){	free(t->cache);	frclear(t, 1);	filedeltext(t->file, t);	t->file = nil;	rfclose(t->reffont);	if(argtext == t)		argtext = nil;	if(typetext == t)		typetext = nil;	if(seltext == t)		seltext = nil;	if(mousetext == t)		mousetext = nil;	if(barttext == t)		barttext = nil;}intdircmp(void *a, void *b){	Dirlist *da, *db;	int i, n;	da = *(Dirlist**)a;	db = *(Dirlist**)b;	n = min(da->nr, db->nr);	i = memcmp(da->r, db->r, n*sizeof(Rune));	if(i)		return i;	return da->nr - db->nr;}voidtextcolumnate(Text *t, Dirlist **dlp, int ndl){	int i, j, w, colw, mint, maxt, ncol, nrow;	Dirlist *dl;	uint q1;	if(t->file->ntext > 1)		return;	mint = stringwidth(t->font, "0");	/* go for narrower tabs if set more than 3 wide */	t->maxtab = min(maxtab, TABDIR)*mint;	maxt = t->maxtab;	colw = 0;	for(i=0; i<ndl; i++){		dl = dlp[i];		w = dl->wid;		if(maxt-w%maxt < mint || w%maxt==0)			w += mint;		if(w % maxt)			w += maxt-(w%maxt);		if(w > colw)			colw = w;	}	if(colw == 0)		ncol = 1;	else		ncol = max(1, Dx(t->r)/colw);	nrow = (ndl+ncol-1)/ncol;	q1 = 0;	for(i=0; i<nrow; i++){		for(j=i; j<ndl; j+=nrow){			dl = dlp[j];			fileinsert(t->file, q1, dl->r, dl->nr);			q1 += dl->nr;			if(j+nrow >= ndl)				break;			w = dl->wid;			if(maxt-w%maxt < mint){				fileinsert(t->file, q1, L"\t", 1);				q1++;				w += mint;			}			do{				fileinsert(t->file, q1, L"\t", 1);				q1++;				w += maxt-(w%maxt);			}while(w < colw);		}		fileinsert(t->file, q1, L"\n", 1);		q1++;	}}uinttextload(Text *t, uint q0, char *file, int setqid){	Rune *rp;	Dirlist *dl, **dlp;	int fd, i, j, n, ndl, nulls;	uint q, q1;	Dir *d, *dbuf;	char *tmp;	Text *u;	if(t->ncache!=0 || t->file->nc || t->w==nil || t!=&t->w->body)		error("text.load");	if(t->w->isdir && t->file->nname==0){		warning(nil, "empty directory name\n");		return 0;	}	fd = open(file, OREAD);	if(fd < 0){		warning(nil, "can't open %s: %r\n", file);		return 0;	}	d = dirfstat(fd);	if(d == nil){		warning(nil, "can't fstat %s: %r\n", file);		goto Rescue;	}	nulls = FALSE;	if(d->qid.type & QTDIR){		/* this is checked in get() but it's possible the file changed underfoot */		if(t->file->ntext > 1){			warning(nil, "%s is a directory; can't read with multiple windows on it\n", file);			goto Rescue;		}		t->w->isdir = TRUE;		t->w->filemenu = FALSE;		if(t->file->name[t->file->nname-1] != '/'){			rp = runemalloc(t->file->nname+1);			runemove(rp, t->file->name, t->file->nname);			rp[t->file->nname] = '/';			winsetname(t->w, rp, t->file->nname+1);			free(rp);		}		dlp = nil;		ndl = 0;		dbuf = nil;		while((n=dirread(fd, &dbuf)) > 0){			for(i=0; i<n; i++){				dl = emalloc(sizeof(Dirlist));				j = strlen(dbuf[i].name);				tmp = emalloc(j+1+1);				memmove(tmp, dbuf[i].name, j);				if(dbuf[i].qid.type & QTDIR)					tmp[j++] = '/';				tmp[j] = '\0';				dl->r = bytetorune(tmp, &dl->nr);				dl->wid = stringwidth(t->font, tmp);				free(tmp);				ndl++;				dlp = realloc(dlp, ndl*sizeof(Dirlist*));				dlp[ndl-1] = dl;			}			free(dbuf);		}		qsort(dlp, ndl, sizeof(Dirlist*), dircmp);		t->w->dlp = dlp;		t->w->ndl = ndl;		textcolumnate(t, dlp, ndl);		q1 = t->file->nc;	}else{		t->w->isdir = FALSE;		t->w->filemenu = TRUE;		q1 = q0 + fileload(t->file, q0, fd, &nulls);	}	if(setqid){		t->file->dev = d->dev;		t->file->mtime = d->mtime;		t->file->qidpath = d->qid.path;	}	close(fd);	rp = fbufalloc();	for(q=q0; q<q1; q+=n){		n = q1-q;		if(n > RBUFSIZE)			n = RBUFSIZE;		bufread(t->file, q, rp, n);		if(q < t->org)			t->org += n;		else if(q <= t->org+t->nchars)			frinsert(t, rp, rp+n, q-t->org);		if(t->lastlinefull)			break;	}	fbuffree(rp);	for(i=0; i<t->file->ntext; i++){		u = t->file->text[i];		if(u != t){			if(u->org > u->file->nc)	/* will be 0 because of reset(), but safety first */				u->org = 0;			textresize(u, u->all);			textbacknl(u, u->org, 0);	/* go to beginning of line */		}		textsetselect(u, q0, q0);	}	if(nulls)		warning(nil, "%s: NUL bytes elided\n", file);	free(d);	return q1-q0;    Rescue:	close(fd);	return 0;}uinttextbsinsert(Text *t, uint q0, Rune *r, uint n, int tofile, int *nrp){	Rune *bp, *tp, *up;	int i, initial;	if(t->what == Tag){	/* can't happen but safety first: mustn't backspace over file name */    Err:		textinsert(t, q0, r, n, tofile);		*nrp = n;		return q0;	}	bp = r;	for(i=0; i<n; i++)		if(*bp++ == '\b'){			--bp;			initial = 0;			tp = runemalloc(n);			runemove(tp, r, i);			up = tp+i;			for(; i<n; i++){				*up = *bp++;				if(*up == '\b')					if(up == tp)						initial++;					else						--up;				else					up++;			}			if(initial){				if(initial > q0)					initial = q0;				q0 -= initial;				textdelete(t, q0, q0+initial, tofile);			}			n = up-tp;			textinsert(t, q0, tp, n, tofile);			free(tp);			*nrp = n;			return q0;		}	goto Err;}voidtextinsert(Text *t, uint q0, Rune *r, uint n, int tofile){	int c, i;	Text *u;	if(tofile && t->ncache != 0)		error("text.insert");	if(n == 0)		return;	if(tofile){		fileinsert(t->file, q0, r, n);		if(t->what == Body){			t->w->dirty = TRUE;			t->w->utflastqid = -1;		}		if(t->file->ntext > 1)			for(i=0; i<t->file->ntext; i++){				u = t->file->text[i];				if(u != t){					u->w->dirty = TRUE;	/* always a body */					textinsert(u, q0, r, n, FALSE);					textsetselect(u, u->q0, u->q1);					textscrdraw(u);				}			}						}	if(q0 < t->q1)		t->q1 += n;	if(q0 < t->q0)		t->q0 += n;	if(q0 < t->org)		t->org += n;	else if(q0 <= t->org+t->nchars)		frinsert(t, r, r+n, q0-t->org);	if(t->w){		c = 'i';		if(t->what == Body)			c = 'I';		if(n <= EVENTSIZE)			winevent(t->w, "%c%d %d 0 %d %.*S\n", c, q0, q0+n, n, n, r);		else			winevent(t->w, "%c%d %d 0 0 \n", c, q0, q0+n, n);	}}voidtypecommit(Text *t){	if(t->w != nil)		wincommit(t->w, t);	else		textcommit(t, TRUE);}voidtextfill(Text *t){	Rune *rp;	int i, n, m, nl;	if(t->lastlinefull || t->nofill)		return;	if(t->ncache > 0)		typecommit(t);	rp = fbufalloc();	do{		n = t->file->nc-(t->org+t->nchars);		if(n == 0)			break;		if(n > 2000)	/* educated guess at reasonable amount */			n = 2000;		bufread(t->file, t->org+t->nchars, rp, n);		/*		 * it's expensive to frinsert more than we need, so		 * count newlines.		 */		nl = t->maxlines-t->nlines;		m = 0;		for(i=0; i<n; ){			if(rp[i++] == '\n'){				m++;				if(m >= nl)					break;			}		}		frinsert(t, rp, rp+i, t->nchars);	}while(t->lastlinefull == FALSE);	fbuffree(rp);}voidtextdelete(Text *t, uint q0, uint q1, int tofile){	uint n, p0, p1;	int i, c;	Text *u;	if(tofile && t->ncache != 0)		error("text.delete");	n = q1-q0;	if(n == 0)		return;	if(tofile){		filedelete(t->file, q0, q1);		if(t->what == Body){			t->w->dirty = TRUE;			t->w->utflastqid = -1;		}		if(t->file->ntext > 1)			for(i=0; i<t->file->ntext; i++){				u = t->file->text[i];				if(u != t){					u->w->dirty = TRUE;	/* always a body */					textdelete(u, q0, q1, FALSE);					textsetselect(u, u->q0, u->q1);					textscrdraw(u);				}			}	}	if(q0 < t->q0)		t->q0 -= min(n, t->q0-q0);	if(q0 < t->q1)		t->q1 -= min(n, t->q1-q0);	if(q1 <= t->org)		t->org -= n;	else if(q0 < t->org+t->nchars){		p1 = q1 - t->org;		if(p1 > t->nchars)			p1 = t->nchars;		if(q0 < t->org){			t->org = q0;			p0 = 0;		}else			p0 = q0 - t->org;		frdelete(t, p0, p1);		textfill(t);	}	if(t->w){		c = 'd';		if(t->what == Body)			c = 'D';		winevent(t->w, "%c%d %d 0 0 \n", c, q0, q1);	}}voidtextconstrain(Text *t, uint q0, uint q1, uint *p0, uint *p1){	*p0 = min(q0, t->file->nc);	*p1 = min(q1, t->file->nc);}Runetextreadc(Text *t, uint q){	Rune r;	if(t->cq0<=q && q<t->cq0+t->ncache)		r = t->cache[q-t->cq0];	else		bufread(t->file, q, &r, 1);	return r;}inttextbswidth(Text *t, Rune c){	uint q, eq;	Rune r;	int skipping;	/* there is known to be at least one character to erase */	if(c == 0x08)	/* ^H: erase character */		return 1;	q = t->q0;	skipping = TRUE;	while(q > 0){		r = textreadc(t, q-1);		if(r == '\n'){		/* eat at most one more character */			if(q == t->q0)	/* eat the newline */				--q;			break; 		}		if(c == 0x17){			eq = isalnum(r);			if(eq && skipping)	/* found one; stop skipping */				skipping = FALSE;			else if(!eq && !skipping)				break;		}		--q;	}	return t->q0-q;}inttextfilewidth(Text *t, uint q0, int oneelement){	uint q;	Rune r;	q = q0;	while(q > 0){		r = textreadc(t, q-1);		if(r <= ' ')			break;		if(oneelement && r=='/')			break;		--q;	}	return q0-q;}Rune*textcomplete(Text *t){	int i, nstr, npath;	uint q;	Rune tmp[200];	Rune *str, *path;	Rune *rp;	Completion *c;	char *s, *dirs;	Runestr dir;	/* control-f: filename completion; works back to white space or / */	if(t->q0<t->file->nc && textreadc(t, t->q0)>' ')	/* must be at end of word */		return nil;	nstr = textfilewidth(t, t->q0, TRUE);	str = runemalloc(nstr);	npath = textfilewidth(t, t->q0-nstr, FALSE);	path = runemalloc(npath);	c = nil;	rp = nil;	dirs = nil;	q = t->q0-nstr;	for(i=0; i<nstr; i++)		str[i] = textreadc(t, q++);	q = t->q0-nstr-npath;	for(i=0; i<npath; i++)		path[i] = textreadc(t, q++);	/* is path rooted? if not, we need to make it relative to window path */	if(npath>0 && path[0]=='/')		dir = (Runestr){path, npath};	else{		dir = dirname(t, nil, 0);		if(dir.nr + 1 + npath > nelem(tmp)){			free(dir.r);			goto Return;		}		if(dir.nr == 0){			dir.nr = 1;			dir.r = runestrdup(L".");		}		runemove(tmp, dir.r, dir.nr);		tmp[dir.nr] = '/';		runemove(tmp+dir.nr+1, path, npath);		free(dir.r);		dir.r = tmp;		dir.nr += 1+npath;		dir = cleanrname(dir);	}	s = smprint("%.*S", nstr, str);	dirs = smprint("%.*S", dir.nr, dir.r);	c = complete(dirs, s);	free(s);	if(c == nil){		warning(nil, "error attempting completion: %r\n");		goto Return;	}	if(!c->advance){		warning(nil, "%.*S%s%.*S*%s\n",			dir.nr, dir.r,			dir.nr>0 && dir.r[dir.nr-1]!='/' ? "/" : "",			nstr, str,			c->nmatch? "" : ": no matches in:");		for(i=0; i<c->nfile; i++)			warning(nil, " %s\n", c->filename[i]);	}	if(c->advance)		rp = runesmprint("%s", c->string);	else		rp = nil;  Return:	freecompletion(c);	free(dirs);	free(str);	free(path);	return rp;}voidtexttype(Text *t, Rune r){	uint q0, q1;	int nnb, nb, n, i;	int nr;	Rune *rp;	Text *u;	if(t->what!=Body && r=='\n')		return;	nr = 1;	rp = &r;	switch(r){	case Kleft:		if(t->q0 > 0){			typecommit(t);			textshow(t, t->q0-1, t->q0-1, TRUE);		}		return;	case Kright:		if(t->q1 < t->file->nc){			typecommit(t);			textshow(t, t->q1+1, t->q1+1, TRUE);		}		return;	case Kdown:		n = t->maxlines/3;		goto case_Down;	case Kscrollonedown:		n = mousescrollsize(t->maxlines);		if(n <= 0)			n = 1;		goto case_Down;	case Kpgdown:		n = 2*t->maxlines/3;	case_Down:		q0 = t->org+frcharofpt(t, Pt(t->r.min.x, t->r.min.y+n*t->font->height));		textsetorigin(t, q0, TRUE);		return;	case Kup:		n = t->maxlines/3;		goto case_Up;	case Kscrolloneup:		n = mousescrollsize(t->maxlines);		goto case_Up;	case Kpgup:		n = 2*t->maxlines/3;	case_Up:		q0 = textbacknl(t, t->org, n);		textsetorigin(t, q0, TRUE);		return;	case Khome:		typecommit(t);		textshow(t, 0, 0, FALSE);		return;	case Kend:		typecommit(t);		textshow(t, t->file->nc, t->file->nc, FALSE);		return;	case 0x01:	/* ^A: beginning of line */		typecommit(t);		/* go to where ^U would erase, if not already at BOL */		nnb = 0;		if(t->q0>0 && textreadc(t, t->q0-1)!='\n')			nnb = textbswidth(t, 0x15);		textshow(t, t->q0-nnb, t->q0-nnb, TRUE);		return;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -