/* * 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 #include #include #include #include #include #include "page.h" static int gspid; /* globals for atexit */ static int gsfd; static void killgs(void); static void killgs(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"); } void spawnreader(void *cp) { int n, fd, pfd[2]; char buf[1024]; recv(cp, &fd); if(pipe(pfd)<0) wexits("pipe failed"); send(cp, &pfd[1]); while((n=read(pfd[0], buf, sizeof buf)) > 0) { write(1, buf, n); write(fd, buf, n); } close(pfd[0]); threadexits(0); } void spawnmonitor(void *cp) { char buf[4096]; char *xbuf; int fd; int n; int out; int first; recv(cp, &fd); out = open("/dev/tty", 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); } threadexits(0); } int spawngs(GSInfo *g, char *safer) { Channel *cp; char *args[16]; char tb[32], gb[32]; int i, nargs; int devnull; int stdinp[2]; int stdoutp[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(stdinp)<0 || pipe(stdoutp)<0 || pipe(dataout)<0 || pipe(errout)<0) return -1; nargs = 0; args[nargs++] = "gs"; args[nargs++] = "-dNOPAUSE"; args[nargs++] = "-dNOPROMPT"; args[nargs++] = "-dDELAYSAFER"; args[nargs++] = "-dQUIET"; args[nargs++] = "-sDEVICE=bmp16m"; args[nargs++] = "-sOutputFile=/dev/fd/3"; 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] = nil; gspid = fork(); if(gspid == 0) { close(stdinp[1]); close(stdoutp[0]); close(dataout[0]); close(errout[0]); /* * 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) ; stdinp[0] = dup(stdinp[0], -1); stdoutp[1] = dup(stdoutp[1], -1); errout[1] = dup(errout[1], -1); dataout[1] = dup(dataout[1], -1); dup(stdinp[0], 0); dup(errout[1], 1); dup(devnull, 2); /* never anything useful */ dup(dataout[1], 3); dup(stdoutp[1], 4); for(i=5; i<20; i++) close(i); execvp("gs", args); wexits("exec"); } close(stdinp[0]); close(stdoutp[1]); close(errout[1]); close(dataout[1]); atexit(killgs); cp = chancreate(sizeof(int), 0); if(teegs) { proccreate(spawnreader, cp, mainstacksize); send(cp, &stdoutp[0]); recv(cp, &stdoutp[0]); } gsfd = g->gsfd = stdinp[1]; g->gspid = gspid; g->g.fd = dataout[0]; g->g.name = "gs pipe"; g->g.type = Ibmp; proccreate(spawnmonitor, cp, mainstacksize); send(cp, &errout[0]); chanfree(cp); Binit(&g->gsrd, stdoutp[0], OREAD); gscmd(g, "/PAGEOUT (/dev/fd/4) (w) file def\n"); if(!strcmp(safer, "-dSAFER")) gscmd(g, ".setsafe\n"); gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n"); waitgs(g); return 0; } int gscmd(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. */ void setdim(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; default: 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); } void waitgs(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); }