351 lines · 6.8 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * gs interface for page. * ps.c and pdf.c both use these routines. * a caveat: if you run more than one gs, only the last  * one gets killed by killgs  */#include <u.h>#include <libc.h>#include <draw.h>#include <event.h>#include <bio.h>#include "page.h" static int gspid;	/* globals for atexit */static int gsfd;static void	killgs(void); static voidkillgs(void){	char tmpfile[100]; 	close(gsfd);	postnote(PNGROUP, getpid(), "die"); 	/*	 * from ghostscript's use.txt:	 * ``Ghostscript currently doesn't do a very good job of deleting temporary	 * files when it exits; you may have to delete them manually from time to	 * time.''	 */	sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000);	if(chatty) fprint(2, "remove %s...\n", tmpfile);	remove(tmpfile);	sleep(100);	postnote(PNPROC, gspid, "die yankee pig dog");} intspawnwriter(GSInfo *g, Biobuf *b){	char buf[4096];	int n;	int fd; 	switch(fork()){	case -1:	return -1;	case 0:	break;	default:	return 0;	} 	Bseek(b, 0, 0);	fd = g->gsfd;	while((n = Bread(b, buf, sizeof buf)) > 0)		write(fd, buf, n);	fprint(fd, "(/fd/3) (w) file dup (THIS IS NOT AN INFERNO BITMAP\\n) writestring flushfile\n");	_exits(0);	return -1;} intspawnreader(int fd){	int n, pfd[2];	char buf[1024]; 	if(pipe(pfd)<0)		return -1;	switch(fork()){	case -1:		return -1;	case 0:		break;	default:		close(pfd[0]);		return pfd[1];	} 	close(pfd[1]);	switch(fork()){	case -1:		wexits("fork failed");	case 0:		while((n=read(fd, buf, sizeof buf)) > 0) {			write(1, buf, n);			write(pfd[0], buf, n);		}		break;	default:		while((n=read(pfd[0], buf, sizeof buf)) > 0) {			write(1, buf, n);			write(fd, buf, n);		}		break;	}	postnote(PNGROUP, getpid(), "i'm die-ing");	_exits(0);	return -1;} voidspawnmonitor(int fd){	char buf[4096];	char *xbuf;	int n;	int out;	int first; 	switch(rfork(RFFDG|RFNOTEG|RFPROC)){	case -1:	default:		return; 	case 0:		break;	} 	out = open("/dev/cons", OWRITE);	if(out < 0)		out = 2; 	xbuf = buf;	/* for ease of acid */	first = 1;	while((n = read(fd, xbuf, sizeof buf)) > 0){		if(first){			first = 0;			fprint(2, "Ghostscript Error:\n");		}		write(out, xbuf, n);		alarm(500);	}	_exits(0);} int spawngs(GSInfo *g, char *safer){	char *args[16];	char tb[32], gb[32];	int i, nargs;	int devnull;	int stdinout[2];	int dataout[2];	int errout[2]; 	/*	 * spawn gs	 * 	 * gs's standard input is fed from stdinout.	 * gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout.	 * gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout.	 * gs data output is written to fd 3, which is dataout.	 */	if(pipe(stdinout) < 0 || pipe(dataout)<0 || pipe(errout)<0)		return -1; 	nargs = 0;	args[nargs++] = "gs";	args[nargs++] = "-dNOPAUSE";	args[nargs++] = safer;	args[nargs++] = "-sDEVICE=plan9";	args[nargs++] = "-sOutputFile=/fd/3";	args[nargs++] = "-dQUIET";	args[nargs++] = "-r100";	sprint(tb, "-dTextAlphaBits=%d", textbits);	sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits);	if(textbits)		args[nargs++] = tb;	if(gfxbits)		args[nargs++] = gb;	args[nargs++] = "-";	args[nargs] = nil; 	gspid = fork();	if(gspid == 0) {		close(stdinout[1]);		close(dataout[1]);		close(errout[1]); 		/*		 * Horrible problem: we want to dup fd's 0-4 below,		 * but some of the source fd's might have those small numbers.		 * So we need to reallocate those.  In order to not step on		 * anything else, we'll dup the fd's to higher ones using		 * dup(x, -1), but we need to use up the lower ones first.		 */		while((devnull = open("/dev/null", ORDWR)) < 5)			; 		stdinout[0] = dup(stdinout[0], -1);		errout[0] = dup(errout[0], -1);		dataout[0] = dup(dataout[0], -1); 		dup(stdinout[0], 0);		dup(errout[0], 1);		dup(devnull, 2);	/* never anything useful */		dup(dataout[0], 3);		dup(stdinout[0], 4);		for(i=5; i<20; i++)			close(i);		exec("/bin/gs", args);		wexits("exec");	}	close(stdinout[0]);	close(errout[0]);	close(dataout[0]);	atexit(killgs); 	if(teegs)		stdinout[1] = spawnreader(stdinout[1]); 	gsfd = g->gsfd = stdinout[1];	g->gsdfd = dataout[1];	g->gspid = gspid; 	spawnmonitor(errout[1]);	Binit(&g->gsrd, g->gsfd, OREAD); 	gscmd(g, "/PAGEOUT (/fd/4) (w) file def\n");	gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n");	waitgs(g); 	return 0;} intgscmd(GSInfo *gs, char *fmt, ...){	char buf[1024];	int n; 	va_list v;	va_start(v, fmt);	n = vseprint(buf, buf+sizeof buf, fmt, v) - buf;	if(n <= 0)		return n; 	if(chatty) {		fprint(2, "cmd: ");		write(2, buf, n);	} 	if(write(gs->gsfd, buf, n) != 0)		return -1; 	return n;} /* * set the dimensions of the bitmap we expect to get back from GS. */voidsetdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape){	Rectangle pbox; 	if(chatty)		fprint(2, "setdim: bbox=%R\n", bbox); 	if(ppi)		gs->ppi = ppi; 	gscmd(gs, "mark\n");	if(ppi)		gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi); 	if(!Dx(bbox))		bbox = Rect(0, 0, 612, 792);	/* 8½×11 */ 	switch(landscape){	case 0:		pbox = bbox;		break;	case 1:		pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x);		break;	}	gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox));	gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y);	gscmd(gs, "currentdevice putdeviceprops pop\n");	gscmd(gs, "/#copies 1 store\n"); 	if(!eqpt(bbox.min, ZP))		gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y); 	switch(landscape){	case 0:		break;	case 1:		gscmd(gs, "%d 0 translate\n", Dy(bbox));		gscmd(gs, "90 rotate\n");		break;	} 	waitgs(gs);} voidwaitgs(GSInfo *gs){	/* we figure out that gs is done by telling it to	 * print something and waiting until it does.	 */	char *p;	Biobuf *b = &gs->gsrd;	uchar buf[1024];	int n; //	gscmd(gs, "(\\n**bstack\\n) print flush\n");//	gscmd(gs, "stack flush\n");//	gscmd(gs, "(**estack\\n) print flush\n");	gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n"); 	alarm(300*1000);	for(;;) {		p = Brdline(b, '\n');		if(p == nil) {			n = Bbuffered(b);			if(n <= 0)				break;			if(n > sizeof buf)				n = sizeof buf;			Bread(b, buf, n);			continue;		}		p[Blinelen(b)-1] = 0;		if(chatty) fprint(2, "p: ");		if(chatty) write(2, p, Blinelen(b)-1);		if(chatty) fprint(2, "\n");		if(strstr(p, "Error:")) {			alarm(0);			fprint(2, "ghostscript error: %s\n", p);			wexits("gs error");		} 		if(strstr(p, "//GO.SYSIN DD")) {			break;		}	}	alarm(0);}