acme

thing1's custom acme build!
Log | Files | Refs | README

acme.c (24679B)


      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 	/* for generating syms in mkfile only: */
     15 	#include <bio.h>
     16 	#include "edit.h"
     17 
     18 void	mousethread(void*);
     19 void	keyboardthread(void*);
     20 void	waitthread(void*);
     21 void	xfidallocthread(void*);
     22 void	newwindowthread(void*);
     23 void	plumbproc(void*);
     24 int	timefmt(Fmt*);
     25 
     26 Reffont	**fontcache;
     27 int		nfontcache;
     28 char		wdir[512] = ".";
     29 Reffont	*reffonts[2];
     30 int		snarffd = -1;
     31 int		mainpid;
     32 int		swapscrollbuttons = FALSE;
     33 char		*mtpt;
     34 
     35 enum{
     36 	NSnarf = 1000	/* less than 1024, I/O buffer size */
     37 };
     38 Rune	snarfrune[NSnarf+1];
     39 
     40 char		*fontnames[2] =
     41 {
     42 	"/lib/font/bit/lucsans/euro.8.font",
     43 	"/lib/font/bit/lucm/unicode.9.font"
     44 };
     45 
     46 Command *command;
     47 
     48 void	shutdownthread(void*);
     49 void	acmeerrorinit(void);
     50 void	readfile(Column*, char*);
     51 static int	shutdown(void*, char*);
     52 
     53 void
     54 derror(Display *d, char *errorstr)
     55 {
     56 	USED(d);
     57 	error(errorstr);
     58 }
     59 
     60 void
     61 threadmain(int argc, char *argv[])
     62 {
     63 	int i;
     64 	char *p, *loadfile = alloca(128);
     65 	Column *c;
     66 	int ncol;
     67 	Display *d;
     68 
     69 	rfork(RFENVG|RFNAMEG);
     70 
     71 	ncol = -1;
     72 
     73 	strcpy(loadfile, ".acme.dump");
     74 	ARGBEGIN{
     75 	case 'D':
     76 		{extern int _threaddebuglevel;
     77 		_threaddebuglevel = ~0;
     78 		}
     79 		break;
     80 	case 'a':
     81 		globalautoindent = TRUE;
     82 		break;
     83 	case 'b':
     84 		bartflag = TRUE;
     85 		break;
     86 	case 'c':
     87 		p = ARGF();
     88 		if(p == nil)
     89 			goto Usage;
     90 		ncol = atoi(p);
     91 		if(ncol <= 0)
     92 			goto Usage;
     93 		break;
     94 	case 'f':
     95 		fontnames[0] = ARGF();
     96 		if(fontnames[0] == nil)
     97 			goto Usage;
     98 		break;
     99 	case 'F':
    100 		fontnames[1] = ARGF();
    101 		if(fontnames[1] == nil)
    102 			goto Usage;
    103 		break;
    104 	case 'l':
    105 		loadfile = ARGF();
    106 		if(loadfile == nil)
    107 			goto Usage;
    108 		break;
    109 	case 'm':
    110 		mtpt = ARGF();
    111 		if(mtpt == nil)
    112 			goto Usage;
    113 		break;
    114 	case 'r':
    115 		swapscrollbuttons = TRUE;
    116 		break;
    117 	case 'W':
    118 		winsize = ARGF();
    119 		if(winsize == nil)
    120 			goto Usage;
    121 		break;
    122 	default:
    123 	Usage:
    124 		fprint(2, "usage: acme -a -c ncol -f fontname -F fixedwidthfontname -l loadfile -W winsize\n");
    125 		threadexitsall("usage");
    126 	}ARGEND
    127 
    128 	fontnames[0] = estrdup(fontnames[0]);
    129 	fontnames[1] = estrdup(fontnames[1]);
    130 
    131 	quotefmtinstall();
    132 	fmtinstall('t', timefmt);
    133 
    134 	cputype = getenv("cputype");
    135 	objtype = getenv("objtype");
    136 	home = getenv("HOME");
    137 	acmeshell = getenv("acmeshell");
    138 	if(acmeshell && *acmeshell == '\0')
    139 		acmeshell = nil;
    140 	p = getenv("tabstop");
    141 	if(p != nil){
    142 		maxtab = strtoul(p, nil, 0);
    143 		free(p);
    144 	}
    145 	if(maxtab == 0)
    146 		maxtab = 4;
    147 	if(loadfile)
    148 		rowloadfonts(loadfile);
    149 	putenv("font", fontnames[0]);
    150 	snarffd = open("/dev/snarf", OREAD|OCEXEC);
    151 /*
    152 	if(cputype){
    153 		sprint(buf, "/acme/bin/%s", cputype);
    154 		bind(buf, "/bin", MBEFORE);
    155 	}
    156 	bind("/acme/bin", "/bin", MBEFORE);
    157 */
    158 	getwd(wdir, sizeof wdir);
    159 
    160 /*
    161 	if(geninitdraw(nil, derror, fontnames[0], "acme", nil, Refnone) < 0){
    162 		fprint(2, "acme: can't open display: %r\n");
    163 		threadexitsall("geninitdraw");
    164 	}
    165 */
    166 	if(initdraw(derror, fontnames[0], "acme") < 0){
    167 		fprint(2, "acme: can't open display: %r\n");
    168 		threadexitsall("initdraw");
    169 	}
    170 
    171 	d = display;
    172 	font = d->defaultfont;
    173 /*assert(font); */
    174 
    175 	reffont.f = font;
    176 	reffonts[0] = &reffont;
    177 	incref(&reffont.ref);	/* one to hold up 'font' variable */
    178 	incref(&reffont.ref);	/* one to hold up reffonts[0] */
    179 	fontcache = emalloc(sizeof(Reffont*));
    180 	nfontcache = 1;
    181 	fontcache[0] = &reffont;
    182 
    183 	iconinit();
    184 	timerinit();
    185 	rxinit();
    186 
    187 	display->white = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x181818FF);	/* set the default bg color */
    188 	
    189 
    190 	cwait = threadwaitchan();
    191 	ccommand = chancreate(sizeof(Command**), 0);
    192 	ckill = chancreate(sizeof(Rune*), 0);
    193 	cxfidalloc = chancreate(sizeof(Xfid*), 0);
    194 	cxfidfree = chancreate(sizeof(Xfid*), 0);
    195 	cnewwindow = chancreate(sizeof(Channel*), 0);
    196 	cerr = chancreate(sizeof(char*), 0);
    197 	cedit = chancreate(sizeof(int), 0);
    198 	cexit = chancreate(sizeof(int), 0);
    199 	cwarn = chancreate(sizeof(void*), 1);
    200 	if(cwait==nil || ccommand==nil || ckill==nil || cxfidalloc==nil || cxfidfree==nil || cerr==nil || cexit==nil || cwarn==nil){
    201 		fprint(2, "acme: can't create initial channels: %r\n");
    202 		threadexitsall("channels");
    203 	}
    204 	chansetname(ccommand, "ccommand");
    205 	chansetname(ckill, "ckill");
    206 	chansetname(cxfidalloc, "cxfidalloc");
    207 	chansetname(cxfidfree, "cxfidfree");
    208 	chansetname(cnewwindow, "cnewwindow");
    209 	chansetname(cerr, "cerr");
    210 	chansetname(cedit, "cedit");
    211 	chansetname(cexit, "cexit");
    212 	chansetname(cwarn, "cwarn");
    213 
    214 	mousectl = initmouse(nil, screen);
    215 	if(mousectl == nil){
    216 		fprint(2, "acme: can't initialize mouse: %r\n");
    217 		threadexitsall("mouse");
    218 	}
    219 	mouse = &mousectl->m;
    220 	keyboardctl = initkeyboard(nil);
    221 	if(keyboardctl == nil){
    222 		fprint(2, "acme: can't initialize keyboard: %r\n");
    223 		threadexitsall("keyboard");
    224 	}
    225 	mainpid = getpid();
    226 	startplumbing();
    227 /*
    228 	plumbeditfd = plumbopen("edit", OREAD|OCEXEC);
    229 	if(plumbeditfd < 0)
    230 		fprint(2, "acme: can't initialize plumber: %r\n");
    231 	else{
    232 		cplumb = chancreate(sizeof(Plumbmsg*), 0);
    233 		threadcreate(plumbproc, nil, STACK);
    234 	}
    235 	plumbsendfd = plumbopen("send", OWRITE|OCEXEC);
    236 */
    237 
    238 	fsysinit();
    239 
    240 	#define	WPERCOL	8
    241 	disk = diskinit();
    242 	if(!loadfile || !rowload(&row, loadfile, TRUE)){
    243 		rowinit(&row, screen->clipr);
    244 		if(ncol < 0){
    245 			if(argc == 0)
    246 				ncol = 2;
    247 			else{
    248 				ncol = (argc+(WPERCOL-1))/WPERCOL;
    249 				if(ncol < 2)
    250 					ncol = 2;
    251 			}
    252 		}
    253 		if(ncol == 0)
    254 			ncol = 2;
    255 		for(i=0; i<ncol; i++){
    256 			c = rowadd(&row, nil, -1);
    257 			if(c==nil && i==0)
    258 				error("initializing columns");
    259 		}
    260 		c = row.col[row.ncol-1];
    261 		if(argc == 0)
    262 			readfile(c, wdir);
    263 		else
    264 			for(i=0; i<argc; i++){
    265 				p = utfrrune(argv[i], '/');
    266 				if((p!=nil && strcmp(p, "/guide")==0) || i/WPERCOL>=row.ncol)
    267 					readfile(c, argv[i]);
    268 				else
    269 					readfile(row.col[i/WPERCOL], argv[i]);
    270 			}
    271 	}
    272 	flushimage(display, 1);
    273 
    274 	acmeerrorinit();
    275 	threadcreate(keyboardthread, nil, STACK);
    276 	threadcreate(mousethread, nil, STACK);
    277 	threadcreate(waitthread, nil, STACK);
    278 	threadcreate(xfidallocthread, nil, STACK);
    279 	threadcreate(newwindowthread, nil, STACK);
    280 /*	threadcreate(shutdownthread, nil, STACK); */
    281 	threadnotify(shutdown, 1);
    282 	recvul(cexit);
    283 	killprocs();
    284 	threadexitsall(nil);
    285 }
    286 
    287 void
    288 readfile(Column *c, char *s)
    289 {
    290 	Window *w;
    291 	Rune rb[256];
    292 	int nr;
    293 	Runestr rs;
    294 
    295 	w = coladd(c, nil, nil, -1);
    296 	if(s[0] != '/')
    297 		runesnprint(rb, sizeof rb, "%s/%s", wdir, s);
    298 	else
    299 		runesnprint(rb, sizeof rb, "%s", s);
    300 	nr = runestrlen(rb);
    301 	rs = cleanrname(runestr(rb, nr));
    302 	winsetname(w, rs.r, rs.nr);
    303 	textload(&w->body, 0, s, 1);
    304 	w->body.file->mod = FALSE;
    305 	w->dirty = FALSE;
    306 	winsettag(w);
    307 	winresize(w, w->r, FALSE, TRUE);
    308 	textscrdraw(&w->body);
    309 	textsetselect(&w->tag, w->tag.file->b.nc, w->tag.file->b.nc);
    310 	xfidlog(w, "new");
    311 }
    312 
    313 char *ignotes[] = {
    314 	"sys: write on closed pipe",
    315 	"sys: ttin",
    316 	"sys: ttou",
    317 	"sys: tstp",
    318 	nil
    319 };
    320 
    321 char *oknotes[] ={
    322 	"delete",
    323 	"hangup",
    324 	"kill",
    325 	"exit",
    326 	nil
    327 };
    328 
    329 int	dumping;
    330 
    331 static int
    332 shutdown(void *v, char *msg)
    333 {
    334 	int i;
    335 
    336 	USED(v);
    337 
    338 	for(i=0; ignotes[i]; i++)
    339 		if(strncmp(ignotes[i], msg, strlen(ignotes[i])) == 0)
    340 			return 1;
    341 
    342 	killprocs();
    343 	if(!dumping && strcmp(msg, "kill")!=0 && strcmp(msg, "exit")!=0 && getpid()==mainpid){
    344 		dumping = TRUE;
    345 		rowdump(&row, nil);
    346 	}
    347 	for(i=0; oknotes[i]; i++)
    348 		if(strncmp(oknotes[i], msg, strlen(oknotes[i])) == 0)
    349 			threadexitsall(msg);
    350 	print("acme: %s\n", msg);
    351 	return 0;
    352 }
    353 
    354 /*
    355 void
    356 shutdownthread(void *v)
    357 {
    358 	char *msg;
    359 	Channel *c;
    360 
    361 	USED(v);
    362 
    363 	threadsetname("shutdown");
    364 	c = threadnotechan();
    365 	while((msg = recvp(c)) != nil)
    366 		shutdown(nil, msg);
    367 }
    368 */
    369 
    370 void
    371 killprocs(void)
    372 {
    373 	Command *c;
    374 
    375 	fsysclose();
    376 /*	if(display) */
    377 /*		flushimage(display, 1); */
    378 
    379 	for(c=command; c; c=c->next)
    380 		postnote(PNGROUP, c->pid, "hangup");
    381 }
    382 
    383 static int errorfd;
    384 int erroutfd;
    385 
    386 void
    387 acmeerrorproc(void *v)
    388 {
    389 	char *buf;
    390 	int n;
    391 
    392 	USED(v);
    393 	threadsetname("acmeerrorproc");
    394 	buf = emalloc(8192+1);
    395 	while((n=read(errorfd, buf, 8192)) >= 0){
    396 		buf[n] = '\0';
    397 		sendp(cerr, estrdup(buf));
    398 	}
    399 	free(buf);
    400 }
    401 
    402 void
    403 acmeerrorinit(void)
    404 {
    405 	int pfd[2];
    406 
    407 	if(pipe(pfd) < 0)
    408 		error("can't create pipe");
    409 #if 0
    410 	sprint(acmeerrorfile, "/srv/acme.%s.%d", getuser(), mainpid);
    411 	fd = create(acmeerrorfile, OWRITE, 0666);
    412 	if(fd < 0){
    413 		remove(acmeerrorfile);
    414   		fd = create(acmeerrorfile, OWRITE, 0666);
    415 		if(fd < 0)
    416 			error("can't create acmeerror file");
    417 	}
    418 	sprint(buf, "%d", pfd[0]);
    419 	write(fd, buf, strlen(buf));
    420 	close(fd);
    421 	/* reopen pfd[1] close on exec */
    422 	sprint(buf, "/fd/%d", pfd[1]);
    423 	errorfd = open(buf, OREAD|OCEXEC);
    424 #endif
    425 	fcntl(pfd[0], F_SETFD, FD_CLOEXEC);
    426 	fcntl(pfd[1], F_SETFD, FD_CLOEXEC);
    427 	erroutfd = pfd[0];
    428 	errorfd = pfd[1];
    429 	if(errorfd < 0)
    430 		error("can't re-open acmeerror file");
    431 	proccreate(acmeerrorproc, nil, STACK);
    432 }
    433 
    434 /*
    435 void
    436 plumbproc(void *v)
    437 {
    438 	Plumbmsg *m;
    439 
    440 	USED(v);
    441 	threadsetname("plumbproc");
    442 	for(;;){
    443 		m = threadplumbrecv(plumbeditfd);
    444 		if(m == nil)
    445 			threadexits(nil);
    446 		sendp(cplumb, m);
    447 	}
    448 }
    449 */
    450 
    451 void
    452 keyboardthread(void *v)
    453 {
    454 	Rune r;
    455 	Timer *timer;
    456 	Text *t;
    457 	enum { KTimer, KKey, NKALT };
    458 	static Alt alts[NKALT+1];
    459 
    460 	USED(v);
    461 	alts[KTimer].c = nil;
    462 	alts[KTimer].v = nil;
    463 	alts[KTimer].op = CHANNOP;
    464 	alts[KKey].c = keyboardctl->c;
    465 	alts[KKey].v = &r;
    466 	alts[KKey].op = CHANRCV;
    467 	alts[NKALT].op = CHANEND;
    468 
    469 	timer = nil;
    470 	typetext = nil;
    471 	threadsetname("keyboardthread");
    472 	for(;;){
    473 		switch(alt(alts)){
    474 		case KTimer:
    475 			timerstop(timer);
    476 			t = typetext;
    477 			if(t!=nil && t->what==Tag){
    478 				winlock(t->w, 'K');
    479 				wincommit(t->w, t);
    480 				winunlock(t->w);
    481 				flushimage(display, 1);
    482 			}
    483 			alts[KTimer].c = nil;
    484 			alts[KTimer].op = CHANNOP;
    485 			break;
    486 		case KKey:
    487 		casekeyboard:
    488 			typetext = rowtype(&row, r, mouse->xy);
    489 			t = typetext;
    490 			if(t!=nil && t->col!=nil && !(r==Kdown || r==Kleft || r==Kright))	/* scrolling doesn't change activecol */
    491 				activecol = t->col;
    492 			if(t!=nil && t->w!=nil)
    493 				t->w->body.file->curtext = &t->w->body;
    494 			if(timer != nil)
    495 				timercancel(timer);
    496 			if(t!=nil && t->what==Tag) {
    497 				timer = timerstart(500);
    498 				alts[KTimer].c = timer->c;
    499 				alts[KTimer].op = CHANRCV;
    500 			}else{
    501 				timer = nil;
    502 				alts[KTimer].c = nil;
    503 				alts[KTimer].op = CHANNOP;
    504 			}
    505 			if(nbrecv(keyboardctl->c, &r) > 0)
    506 				goto casekeyboard;
    507 			flushimage(display, 1);
    508 			break;
    509 		}
    510 	}
    511 }
    512 
    513 void
    514 mousethread(void *v)
    515 {
    516 	Text *t, *argt;
    517 	int but;
    518 	uint q0, q1;
    519 	Window *w;
    520 	Plumbmsg *pm;
    521 	Mouse m;
    522 	char *act;
    523 	enum { MResize, MMouse, MPlumb, MWarnings, NMALT };
    524 	enum { Shift = 5 };
    525 	static Alt alts[NMALT+1];
    526 
    527 	USED(v);
    528 	threadsetname("mousethread");
    529 	alts[MResize].c = mousectl->resizec;
    530 	alts[MResize].v = nil;
    531 	alts[MResize].op = CHANRCV;
    532 	alts[MMouse].c = mousectl->c;
    533 	alts[MMouse].v = &mousectl->m;
    534 	alts[MMouse].op = CHANRCV;
    535 	alts[MPlumb].c = cplumb;
    536 	alts[MPlumb].v = &pm;
    537 	alts[MPlumb].op = CHANRCV;
    538 	alts[MWarnings].c = cwarn;
    539 	alts[MWarnings].v = nil;
    540 	alts[MWarnings].op = CHANRCV;
    541 	if(cplumb == nil)
    542 		alts[MPlumb].op = CHANNOP;
    543 	alts[NMALT].op = CHANEND;
    544 
    545 	for(;;){
    546 		qlock(&row.lk);
    547 		flushwarnings();
    548 		qunlock(&row.lk);
    549 		flushimage(display, 1);
    550 		switch(alt(alts)){
    551 		case MResize:
    552 			if(getwindow(display, Refnone) < 0)
    553 				error("attach to window");
    554 			draw(screen, screen->r, display->white, nil, ZP);
    555 			iconinit();
    556 			scrlresize();
    557 			rowresize(&row, screen->clipr);
    558 			break;
    559 		case MPlumb:
    560 			if(strcmp(pm->type, "text") == 0){
    561 				act = plumblookup(pm->attr, "action");
    562 				if(act==nil || strcmp(act, "showfile")==0)
    563 					plumblook(pm);
    564 				else if(strcmp(act, "showdata")==0)
    565 					plumbshow(pm);
    566 			}
    567 			plumbfree(pm);
    568 			break;
    569 		case MWarnings:
    570 			break;
    571 		case MMouse:
    572 			/*
    573 			 * Make a copy so decisions are consistent; mousectl changes
    574 			 * underfoot.  Can't just receive into m because this introduces
    575 			 * another race; see /sys/src/libdraw/mouse.c.
    576 			 */
    577 			m = mousectl->m;
    578 			qlock(&row.lk);
    579 			t = rowwhich(&row, m.xy);
    580 
    581 			if((t!=mousetext && t!=nil && t->w!=nil) &&
    582 				(mousetext==nil || mousetext->w==nil || t->w->id!=mousetext->w->id)) {
    583 				xfidlog(t->w, "focus");
    584 			}
    585 
    586 			if(t!=mousetext && mousetext!=nil && mousetext->w!=nil){
    587 				winlock(mousetext->w, 'M');
    588 				mousetext->eq0 = ~0;
    589 				wincommit(mousetext->w, mousetext);
    590 				winunlock(mousetext->w);
    591 			}
    592 			mousetext = t;
    593 			if(t == nil)
    594 				goto Continue;
    595 			w = t->w;
    596 			if(t==nil || m.buttons==0)
    597 				goto Continue;
    598 			but = 0;
    599 			if(m.buttons == 1)
    600 				but = 1;
    601 			else if(m.buttons == 2)
    602 				but = 2;
    603 			else if(m.buttons == 4)
    604 				but = 3;
    605 			barttext = t;
    606 			if(t->what==Body && ptinrect(m.xy, t->scrollr)){
    607 				if(but){
    608 					if(swapscrollbuttons){
    609 						if(but == 1)
    610 							but = 3;
    611 						else if(but == 3)
    612 							but = 1;
    613 					}
    614 					winlock(w, 'M');
    615 					t->eq0 = ~0;
    616 					textscroll(t, but);
    617 					winunlock(w);
    618 				}
    619 				goto Continue;
    620 			}
    621 			/* scroll buttons, wheels, etc. */
    622 			if(w != nil && (m.buttons & (8|16))){
    623 				if(m.buttons & 8)
    624 					but = Kscrolloneup;
    625 				else
    626 					but = Kscrollonedown;
    627 				winlock(w, 'M');
    628 				t->eq0 = ~0;
    629 				texttype(t, but);
    630 				winunlock(w);
    631 				goto Continue;
    632 			}
    633 			if(ptinrect(m.xy, t->scrollr)){
    634 				if(but){
    635 					if(t->what == Columntag)
    636 						rowdragcol(&row, t->col, but);
    637 					else if(t->what == Tag){
    638 						coldragwin(t->col, t->w, but);
    639 						if(t->w)
    640 							barttext = &t->w->body;
    641 					}
    642 					if(t->col)
    643 						activecol = t->col;
    644 				}
    645 				goto Continue;
    646 			}
    647 			if(m.buttons){
    648 				if(w)
    649 					winlock(w, 'M');
    650 				t->eq0 = ~0;
    651 				if(w)
    652 					wincommit(w, t);
    653 				else
    654 					textcommit(t, TRUE);
    655 				if(m.buttons & 1){
    656 					textselect(t);
    657 					if(w)
    658 						winsettag(w);
    659 					argtext = t;
    660 					seltext = t;
    661 					if(t->col)
    662 						activecol = t->col;	/* button 1 only */
    663 					if(t->w!=nil && t==&t->w->body)
    664 						activewin = t->w;
    665 				}else if(m.buttons & 2){
    666 					if(textselect2(t, &q0, &q1, &argt))
    667 						execute(t, q0, q1, FALSE, argt);
    668 				}else if(m.buttons & (4|(4<<Shift))){
    669 					if(textselect3(t, &q0, &q1))
    670 						look3(t, q0, q1, FALSE, (m.buttons&(4<<Shift))!=0);
    671 				}
    672 				if(w)
    673 					winunlock(w);
    674 				goto Continue;
    675 			}
    676     Continue:
    677 			qunlock(&row.lk);
    678 			break;
    679 		}
    680 	}
    681 }
    682 
    683 /*
    684  * There is a race between process exiting and our finding out it was ever created.
    685  * This structure keeps a list of processes that have exited we haven't heard of.
    686  */
    687 typedef struct Pid Pid;
    688 struct Pid
    689 {
    690 	int	pid;
    691 	char	msg[ERRMAX];
    692 	Pid	*next;
    693 };
    694 
    695 void
    696 waitthread(void *v)
    697 {
    698 	Waitmsg *w;
    699 	Command *c, *lc;
    700 	uint pid;
    701 	int found, ncmd;
    702 	Rune *cmd;
    703 	char *err;
    704 	Text *t;
    705 	Pid *pids, *p, *lastp;
    706 	enum { WErr, WKill, WWait, WCmd, NWALT };
    707 	Alt alts[NWALT+1];
    708 
    709 	USED(v);
    710 	threadsetname("waitthread");
    711 	pids = nil;
    712 	alts[WErr].c = cerr;
    713 	alts[WErr].v = &err;
    714 	alts[WErr].op = CHANRCV;
    715 	alts[WKill].c = ckill;
    716 	alts[WKill].v = &cmd;
    717 	alts[WKill].op = CHANRCV;
    718 	alts[WWait].c = cwait;
    719 	alts[WWait].v = &w;
    720 	alts[WWait].op = CHANRCV;
    721 	alts[WCmd].c = ccommand;
    722 	alts[WCmd].v = &c;
    723 	alts[WCmd].op = CHANRCV;
    724 	alts[NWALT].op = CHANEND;
    725 
    726 	command = nil;
    727 	for(;;){
    728 		switch(alt(alts)){
    729 		case WErr:
    730 			qlock(&row.lk);
    731 			warning(nil, "%s", err);
    732 			free(err);
    733 			flushimage(display, 1);
    734 			qunlock(&row.lk);
    735 			break;
    736 		case WKill:
    737 			found = FALSE;
    738 			ncmd = runestrlen(cmd);
    739 			for(c=command; c; c=c->next){
    740 				/* -1 for blank */
    741 				if(runeeq(c->name, c->nname-1, cmd, ncmd) == TRUE){
    742 					if(postnote(PNGROUP, c->pid, "kill") < 0)
    743 						warning(nil, "kill %S: %r\n", cmd);
    744 					found = TRUE;
    745 				}
    746 			}
    747 			if(!found)
    748 				warning(nil, "Kill: no process %S\n", cmd);
    749 			free(cmd);
    750 			break;
    751 		case WWait:
    752 			pid = w->pid;
    753 			lc = nil;
    754 			for(c=command; c; c=c->next){
    755 				if(c->pid == pid){
    756 					if(lc)
    757 						lc->next = c->next;
    758 					else
    759 						command = c->next;
    760 					break;
    761 				}
    762 				lc = c;
    763 			}
    764 			qlock(&row.lk);
    765 			t = &row.tag;
    766 			textcommit(t, TRUE);
    767 			if(c == nil){
    768 				/* helper processes use this exit status */
    769 				if(strncmp(w->msg, "libthread", 9) != 0){
    770 					p = emalloc(sizeof(Pid));
    771 					p->pid = pid;
    772 					strncpy(p->msg, w->msg, sizeof(p->msg));
    773 					p->next = pids;
    774 					pids = p;
    775 				}
    776 			}else{
    777 				if(search(t, c->name, c->nname, FALSE)){
    778 					textdelete(t, t->q0, t->q1, TRUE);
    779 					textsetselect(t, 0, 0);
    780 				}
    781 				if(w->msg[0])
    782 					warning(c->md, "%.*S: exit %s\n", c->nname-1, c->name, w->msg);
    783 				flushimage(display, 1);
    784 			}
    785 			qunlock(&row.lk);
    786 			free(w);
    787     Freecmd:
    788 			if(c){
    789 				if(c->iseditcmd)
    790 					sendul(cedit, 0);
    791 				free(c->text);
    792 				free(c->name);
    793 				fsysdelid(c->md);
    794 				free(c);
    795 			}
    796 			break;
    797 		case WCmd:
    798 			/* has this command already exited? */
    799 			lastp = nil;
    800 			for(p=pids; p!=nil; p=p->next){
    801 				if(p->pid == c->pid){
    802 					if(p->msg[0])
    803 						warning(c->md, "%s\n", p->msg);
    804 					if(lastp == nil)
    805 						pids = p->next;
    806 					else
    807 						lastp->next = p->next;
    808 					free(p);
    809 					goto Freecmd;
    810 				}
    811 				lastp = p;
    812 			}
    813 			c->next = command;
    814 			command = c;
    815 			qlock(&row.lk);
    816 			t = &row.tag;
    817 			textcommit(t, TRUE);
    818 			textinsert(t, 0, c->name, c->nname, TRUE);
    819 			textsetselect(t, 0, 0);
    820 			flushimage(display, 1);
    821 			qunlock(&row.lk);
    822 			break;
    823 		}
    824 	}
    825 }
    826 
    827 void
    828 xfidallocthread(void *v)
    829 {
    830 	Xfid *xfree, *x;
    831 	enum { Alloc, Free, N };
    832 	static Alt alts[N+1];
    833 
    834 	USED(v);
    835 	threadsetname("xfidallocthread");
    836 	alts[Alloc].c = cxfidalloc;
    837 	alts[Alloc].v = nil;
    838 	alts[Alloc].op = CHANRCV;
    839 	alts[Free].c = cxfidfree;
    840 	alts[Free].v = &x;
    841 	alts[Free].op = CHANRCV;
    842 	alts[N].op = CHANEND;
    843 
    844 	xfree = nil;
    845 	for(;;){
    846 		switch(alt(alts)){
    847 		case Alloc:
    848 			x = xfree;
    849 			if(x)
    850 				xfree = x->next;
    851 			else{
    852 				x = emalloc(sizeof(Xfid));
    853 				x->c = chancreate(sizeof(void(*)(Xfid*)), 0);
    854 				chansetname(x->c, "xc%p", x->c);
    855 				x->arg = x;
    856 				threadcreate(xfidctl, x->arg, STACK);
    857 			}
    858 			sendp(cxfidalloc, x);
    859 			break;
    860 		case Free:
    861 			x->next = xfree;
    862 			xfree = x;
    863 			break;
    864 		}
    865 	}
    866 }
    867 
    868 /* this thread, in the main proc, allows fsysproc to get a window made without doing graphics */
    869 void
    870 newwindowthread(void *v)
    871 {
    872 	Window *w;
    873 
    874 	USED(v);
    875 	threadsetname("newwindowthread");
    876 
    877 	for(;;){
    878 		/* only fsysproc is talking to us, so synchronization is trivial */
    879 		recvp(cnewwindow);
    880 		w = makenewwindow(nil);
    881 		winsettag(w);
    882 		xfidlog(w, "new");
    883 		sendp(cnewwindow, w);
    884 	}
    885 }
    886 
    887 Reffont*
    888 rfget(int fix, int save, int setfont, char *name)
    889 {
    890 	Reffont *r;
    891 	Font *f;
    892 	int i;
    893 
    894 	r = nil;
    895 	if(name == nil){
    896 		name = fontnames[fix];
    897 		r = reffonts[fix];
    898 	}
    899 	if(r == nil){
    900 		for(i=0; i<nfontcache; i++)
    901 			if(strcmp(name, fontcache[i]->f->name) == 0){
    902 				r = fontcache[i];
    903 				goto Found;
    904 			}
    905 		f = openfont(display, name);
    906 		if(f == nil){
    907 			warning(nil, "can't open font file %s: %r\n", name);
    908 			return nil;
    909 		}
    910 		r = emalloc(sizeof(Reffont));
    911 		r->f = f;
    912 		fontcache = erealloc(fontcache, (nfontcache+1)*sizeof(Reffont*));
    913 		fontcache[nfontcache++] = r;
    914 	}
    915     Found:
    916 	if(save){
    917 		incref(&r->ref);
    918 		if(reffonts[fix])
    919 			rfclose(reffonts[fix]);
    920 		reffonts[fix] = r;
    921 		if(name != fontnames[fix]){
    922 			free(fontnames[fix]);
    923 			fontnames[fix] = estrdup(name);
    924 		}
    925 	}
    926 	if(setfont){
    927 		reffont.f = r->f;
    928 		incref(&r->ref);
    929 		rfclose(reffonts[0]);
    930 		font = r->f;
    931 		reffonts[0] = r;
    932 		incref(&r->ref);
    933 		iconinit();
    934 	}
    935 	incref(&r->ref);
    936 	return r;
    937 }
    938 
    939 void
    940 rfclose(Reffont *r)
    941 {
    942 	int i;
    943 
    944 	if(decref(&r->ref) == 0){
    945 		for(i=0; i<nfontcache; i++)
    946 			if(r == fontcache[i])
    947 				break;
    948 		if(i >= nfontcache)
    949 			warning(nil, "internal error: can't find font in cache\n");
    950 		else{
    951 			nfontcache--;
    952 			memmove(fontcache+i, fontcache+i+1, (nfontcache-i)*sizeof(Reffont*));
    953 		}
    954 		freefont(r->f);
    955 		free(r);
    956 	}
    957 }
    958 
    959 Cursor boxcursor = {
    960 	{-7, -7},
    961 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    962 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
    963 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
    964 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
    965 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
    966 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
    967 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
    968 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
    969 };
    970 
    971 Cursor2 boxcursor2 = {
    972 	{-15, -15},
    973 	{0xFF, 0xFF, 0xFF, 0xFF,
    974 	 0xFF, 0xFF, 0xFF, 0xFF,
    975 	 0xFF, 0xFF, 0xFF, 0xFF,
    976 	 0xFF, 0xFF, 0xFF, 0xFF,
    977 	 0xFF, 0xFF, 0xFF, 0xFF,
    978 	 0xFF, 0xFF, 0xFF, 0xFF,
    979 	 0xFF, 0xFF, 0xFF, 0xFF,
    980 	 0xFF, 0xFF, 0xFF, 0xFF,
    981 	 0xFF, 0xFF, 0xFF, 0xFF,
    982 	 0xFF, 0xFF, 0xFF, 0xFF,
    983 	 0xFF, 0xC0, 0x03, 0xFF,
    984 	 0xFF, 0xC0, 0x03, 0xFF,
    985 	 0xFF, 0xC0, 0x03, 0xFF,
    986 	 0xFF, 0xC0, 0x03, 0xFF,
    987 	 0xFF, 0xC0, 0x03, 0xFF,
    988 	 0xFF, 0xC0, 0x03, 0xFF,
    989 	 0xFF, 0xC0, 0x03, 0xFF,
    990 	 0xFF, 0xC0, 0x03, 0xFF,
    991 	 0xFF, 0xC0, 0x03, 0xFF,
    992 	 0xFF, 0xC0, 0x03, 0xFF,
    993 	 0xFF, 0xC0, 0x03, 0xFF,
    994 	 0xFF, 0xC0, 0x03, 0xFF,
    995 	 0xFF, 0xFF, 0xFF, 0xFF,
    996 	 0xFF, 0xFF, 0xFF, 0xFF,
    997 	 0xFF, 0xFF, 0xFF, 0xFF,
    998 	 0xFF, 0xFF, 0xFF, 0xFF,
    999 	 0xFF, 0xFF, 0xFF, 0xFF,
   1000 	 0xFF, 0xFF, 0xFF, 0xFF,
   1001 	 0xFF, 0xFF, 0xFF, 0xFF,
   1002 	 0xFF, 0xFF, 0xFF, 0xFF,
   1003 	 0xFF, 0xFF, 0xFF, 0xFF,
   1004 	 0xFF, 0xFF, 0xFF, 0xFF},
   1005 	{0x00, 0x00, 0x00, 0x00,
   1006 	 0x00, 0x00, 0x00, 0x00,
   1007 	 0x3F, 0xFF, 0xFF, 0xFC,
   1008 	 0x3F, 0xFF, 0xFF, 0xFC,
   1009 	 0x3F, 0xFF, 0xFF, 0xFC,
   1010 	 0x3F, 0xFF, 0xFF, 0xFC,
   1011 	 0x3F, 0xFF, 0xFF, 0xFC,
   1012 	 0x3F, 0xFF, 0xFF, 0xFC,
   1013 	 0x3F, 0x00, 0x00, 0xFC,
   1014 	 0x3F, 0x00, 0x00, 0xFC,
   1015 	 0x3F, 0x00, 0x00, 0xFC,
   1016 	 0x3F, 0x00, 0x00, 0xFC,
   1017 	 0x3F, 0x00, 0x00, 0xFC,
   1018 	 0x3F, 0x00, 0x00, 0xFC,
   1019 	 0x3F, 0x00, 0x00, 0xFC,
   1020 	 0x3F, 0x00, 0x00, 0xFC,
   1021 	 0x3F, 0x00, 0x00, 0xFC,
   1022 	 0x3F, 0x00, 0x00, 0xFC,
   1023 	 0x3F, 0x00, 0x00, 0xFC,
   1024 	 0x3F, 0x00, 0x00, 0xFC,
   1025 	 0x3F, 0x00, 0x00, 0xFC,
   1026 	 0x3F, 0x00, 0x00, 0xFC,
   1027 	 0x3F, 0x00, 0x00, 0xFC,
   1028 	 0x3F, 0x00, 0x00, 0xFC,
   1029 	 0x3F, 0xFF, 0xFF, 0xFC,
   1030 	 0x3F, 0xFF, 0xFF, 0xFC,
   1031 	 0x3F, 0xFF, 0xFF, 0xFC,
   1032 	 0x3F, 0xFF, 0xFF, 0xFC,
   1033 	 0x3F, 0xFF, 0xFF, 0xFC,
   1034 	 0x3F, 0xFF, 0xFF, 0xFC,
   1035 	 0x00, 0x00, 0x00, 0x00,
   1036 	 0x00, 0x00, 0x00, 0x00}
   1037 };
   1038 
   1039 void
   1040 iconinit(void)
   1041 {
   1042 	Rectangle r;
   1043 	Image *tmp;
   1044 
   1045 	if(tagcols[BACK] == nil) {
   1046 		/* Blue */
   1047 		tagcols[BACK] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x453D41FF);
   1048 		tagcols[HIGH] = allocimagemix(display, 0x453D41FF, 0xFFFFFF33);
   1049 		tagcols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xD7AF00FF);
   1050 		tagcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,  0xF4F4FFFF);
   1051 		tagcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,  0xF4F4FFFF);
   1052 
   1053 		/* Yellow */
   1054 		textcols[BACK]  = allocimage(display, Rect(0,0,1,1), screen->chan, 1,  0x181818FF);
   1055 		textcols[HIGH]  = allocimagemix(display, 0x181818FF, 0xFFFFFF33);
   1056 		textcols[BORD]  = allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xD7AF00FF);
   1057 		textcols[TEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,  0xF4F4FFFF);
   1058 		textcols[HTEXT] = allocimage(display, Rect(0,0,1,1), screen->chan, 1,  0xF4F4FFFF);
   1059 	}
   1060 
   1061 	r = Rect(0, 0, Scrollwid, font->height+1);
   1062 	if(button && eqrect(r, button->r))
   1063 		return;
   1064 
   1065 	if(button){
   1066 		freeimage(button);
   1067 		freeimage(modbutton);
   1068 		freeimage(colbutton);
   1069 	}
   1070 
   1071 	button = allocimage(display, r, screen->chan, 0, DNofill);
   1072 	draw(button, r, tagcols[BACK], nil, r.min);
   1073 	border(button, r, ButtonBorder, tagcols[BORD], ZP);
   1074 
   1075 	r = button->r;
   1076 	modbutton = allocimage(display, r, screen->chan, 0, DNofill);
   1077 	draw(modbutton, r, tagcols[BACK], nil, r.min);
   1078 	border(modbutton, r, ButtonBorder, tagcols[BORD], ZP);
   1079 	r = insetrect(r, ButtonBorder);
   1080 	tmp = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DMedblue);
   1081 	draw(modbutton, r, tmp, nil, ZP);
   1082 	freeimage(tmp);
   1083 
   1084 	r = button->r;
   1085 	colbutton = allocimage(display, r, screen->chan, 0, DPurpleblue);
   1086 
   1087 	but2col = allocimage(display, r, screen->chan, 1, 0xAA0000FF);
   1088 	but3col = allocimage(display, r, screen->chan, 1, 0x006600FF);
   1089 }
   1090 
   1091 /*
   1092  * /dev/snarf updates when the file is closed, so we must open our own
   1093  * fd here rather than use snarffd
   1094  */
   1095 
   1096 /* rio truncates larges snarf buffers, so this avoids using the
   1097  * service if the string is huge */
   1098 
   1099 #define MAXSNARF 100*1024
   1100 
   1101 void
   1102 acmeputsnarf(void)
   1103 {
   1104 	int i, n;
   1105 	Fmt f;
   1106 	char *s;
   1107 
   1108 	if(snarfbuf.nc==0)
   1109 		return;
   1110 	if(snarfbuf.nc > MAXSNARF)
   1111 		return;
   1112 
   1113 	fmtstrinit(&f);
   1114 	for(i=0; i<snarfbuf.nc; i+=n){
   1115 		n = snarfbuf.nc-i;
   1116 		if(n >= NSnarf)
   1117 			n = NSnarf;
   1118 		bufread(&snarfbuf, i, snarfrune, n);
   1119 		if(fmtprint(&f, "%.*S", n, snarfrune) < 0)
   1120 			break;
   1121 	}
   1122 	s = fmtstrflush(&f);
   1123 	if(s && s[0])
   1124 		putsnarf(s);
   1125 	free(s);
   1126 }
   1127 
   1128 void
   1129 acmegetsnarf(void)
   1130 {
   1131 	char *s;
   1132 	int nb, nr, nulls, len;
   1133 	Rune *r;
   1134 
   1135 	s = getsnarf();
   1136 	if(s == nil || s[0]==0){
   1137 		free(s);
   1138 		return;
   1139 	}
   1140 
   1141 	len = strlen(s);
   1142 	r = runemalloc(len+1);
   1143 	cvttorunes(s, len, r, &nb, &nr, &nulls);
   1144 	bufreset(&snarfbuf);
   1145 	bufinsert(&snarfbuf, 0, r, nr);
   1146 	free(r);
   1147 	free(s);
   1148 }
   1149 
   1150 int
   1151 ismtpt(char *file)
   1152 {
   1153 	int n;
   1154 
   1155 	if(mtpt == nil)
   1156 		return 0;
   1157 
   1158 	/* This is not foolproof, but it will stop a lot of them. */
   1159 	n = strlen(mtpt);
   1160 	return strncmp(file, mtpt, n) == 0 && ((n > 0 && mtpt[n-1] == '/') || file[n] == '/' || file[n] == 0);
   1161 }
   1162 
   1163 int
   1164 timefmt(Fmt *f)
   1165 {
   1166 	Tm *tm;
   1167 
   1168 	tm = localtime(va_arg(f->args, ulong));
   1169 	return fmtprint(f, "%04d/%02d/%02d %02d:%02d:%02d",
   1170 		tm->year+1900, tm->mon+1, tm->mday, tm->hour, tm->min, tm->sec);
   1171 }