/* * 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);}
/*
* 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 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");
}
int
spawnwriter(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;
}
int
spawnreader(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;
}
void
spawnmonitor(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;
}
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;
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);
}
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);
}