Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * 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);
}