URI:
       tgs.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tgs.c (6449B)
       ---
            1 /*
            2  * gs interface for page.
            3  * ps.c and pdf.c both use these routines.
            4  * a caveat: if you run more than one gs, only the last
            5  * one gets killed by killgs
            6  */
            7 #include <u.h>
            8 #include <libc.h>
            9 #include <draw.h>
           10 #include <thread.h>
           11 #include <bio.h>
           12 #include <cursor.h>
           13 #include "page.h"
           14 
           15 static int gspid;        /* globals for atexit */
           16 static int gsfd;
           17 static void        killgs(void);
           18 
           19 static void
           20 killgs(void)
           21 {
           22         char tmpfile[100];
           23 
           24         close(gsfd);
           25         postnote(PNGROUP, getpid(), "die");
           26 
           27         /*
           28          * from ghostscript's use.txt:
           29          * ``Ghostscript currently doesn't do a very good job of deleting temporary
           30          * files when it exits; you may have to delete them manually from time to
           31          * time.''
           32          */
           33         sprint(tmpfile, "/tmp/gs_%.5da", (gspid+300000)%100000);
           34         if(chatty) fprint(2, "remove %s...\n", tmpfile);
           35         remove(tmpfile);
           36         sleep(100);
           37         postnote(PNPROC, gspid, "die yankee pig dog");
           38 }
           39 
           40 void
           41 spawnreader(void *cp)
           42 {
           43         int n, fd, pfd[2];
           44         char buf[1024];
           45 
           46         recv(cp, &fd);
           47 
           48         if(pipe(pfd)<0)
           49                 wexits("pipe failed");
           50 
           51         send(cp, &pfd[1]);
           52 
           53         while((n=read(pfd[0], buf, sizeof buf)) > 0) {
           54                 write(1, buf, n);
           55                 write(fd, buf, n);
           56         }
           57 
           58         close(pfd[0]);
           59         threadexits(0);
           60 }
           61 
           62 void
           63 spawnmonitor(void *cp)
           64 {
           65         char buf[4096];
           66         char *xbuf;
           67         int fd;
           68         int n;
           69         int out;
           70         int first;
           71 
           72         recv(cp, &fd);
           73 
           74         out = open("/dev/tty", OWRITE);
           75         if(out < 0)
           76                 out = 2;
           77 
           78         xbuf = buf;        /* for ease of acid */
           79         first = 1;
           80         while((n = read(fd, xbuf, sizeof buf)) > 0){
           81                 if(first){
           82                         first = 0;
           83                         fprint(2, "Ghostscript Error:\n");
           84                 }
           85                 write(out, xbuf, n);
           86                 alarm(500);
           87         }
           88         threadexits(0);
           89 }
           90 
           91 int
           92 spawngs(GSInfo *g, char *safer)
           93 {
           94         Channel *cp;
           95         char *args[16];
           96         char tb[32], gb[32];
           97         int i, nargs;
           98         int devnull;
           99         int stdinp[2];
          100         int stdoutp[2];
          101         int dataout[2];
          102         int errout[2];
          103 
          104         /*
          105          * spawn gs
          106          *
          107           * gs's standard input is fed from stdinout.
          108          * gs output written to fd-2 (i.e. output we generate intentionally) is fed to stdinout.
          109          * gs output written to fd 1 (i.e. ouptut gs generates on error) is fed to errout.
          110          * gs data output is written to fd 3, which is dataout.
          111          */
          112         if(pipe(stdinp)<0 || pipe(stdoutp)<0 || pipe(dataout)<0 || pipe(errout)<0)
          113                 return -1;
          114 
          115         nargs = 0;
          116         args[nargs++] = "gs";
          117         args[nargs++] = "-dNOPAUSE";
          118         args[nargs++] = "-dNOPROMPT";
          119         args[nargs++] = "-dDELAYSAFER";
          120         args[nargs++] = "-dQUIET";
          121         args[nargs++] = "-sDEVICE=bmp16m";
          122         args[nargs++] = "-sOutputFile=/dev/fd/3";
          123         args[nargs++] = "-r100";
          124         sprint(tb, "-dTextAlphaBits=%d", textbits);
          125         sprint(gb, "-dGraphicsAlphaBits=%d", gfxbits);
          126         if(textbits)
          127                 args[nargs++] = tb;
          128         if(gfxbits)
          129                 args[nargs++] = gb;
          130         args[nargs] = nil;
          131 
          132         gspid = fork();
          133         if(gspid == 0) {
          134                 close(stdinp[1]);
          135                 close(stdoutp[0]);
          136                 close(dataout[0]);
          137                 close(errout[0]);
          138 
          139                 /*
          140                  * Horrible problem: we want to dup fd's 0-4 below,
          141                  * but some of the source fd's might have those small numbers.
          142                  * So we need to reallocate those.  In order to not step on
          143                  * anything else, we'll dup the fd's to higher ones using
          144                  * dup(x, -1), but we need to use up the lower ones first.
          145                  */
          146                 while((devnull = open("/dev/null", ORDWR)) < 5)
          147                         ;
          148 
          149                 stdinp[0] = dup(stdinp[0], -1);
          150                 stdoutp[1] = dup(stdoutp[1], -1);
          151                 errout[1] = dup(errout[1], -1);
          152                 dataout[1] = dup(dataout[1], -1);
          153 
          154                 dup(stdinp[0], 0);
          155                 dup(errout[1], 1);
          156                 dup(devnull, 2);        /* never anything useful */
          157                 dup(dataout[1], 3);
          158                 dup(stdoutp[1], 4);
          159                 for(i=5; i<20; i++)
          160                         close(i);
          161                 execvp("gs", args);
          162                 wexits("exec");
          163         }
          164         close(stdinp[0]);
          165         close(stdoutp[1]);
          166         close(errout[1]);
          167         close(dataout[1]);
          168         atexit(killgs);
          169 
          170         cp = chancreate(sizeof(int), 0);
          171         if(teegs) {
          172                 proccreate(spawnreader, cp, mainstacksize);
          173                 send(cp, &stdoutp[0]);
          174                 recv(cp, &stdoutp[0]);
          175         }
          176 
          177         gsfd = g->gsfd = stdinp[1];
          178         g->gspid = gspid;
          179         g->g.fd = dataout[0];
          180         g->g.name = "gs pipe";
          181         g->g.type = Ibmp;
          182 
          183         proccreate(spawnmonitor, cp, mainstacksize);
          184         send(cp, &errout[0]);
          185         chanfree(cp);
          186 
          187         Binit(&g->gsrd, stdoutp[0], OREAD);
          188 
          189         gscmd(g, "/PAGEDIDSHOWPAGE false def\n");
          190         gscmd(g, "/showpage { /PAGEDIDSHOWPAGE true def showpage } bind def\n");
          191         gscmd(g, "/PAGEFLUSH { PAGEDIDSHOWPAGE not {showpage} if /PAGEDIDSHOWPAGE false def } def\n");
          192 
          193         gscmd(g, "/PAGEOUT (/dev/fd/4) (w) file def\n");
          194         if(!strcmp(safer, "-dSAFER"))
          195                 gscmd(g, ".setsafe\n");
          196         gscmd(g, "/PAGE== { PAGEOUT exch write==only PAGEOUT (\\n) writestring PAGEOUT flushfile } def\n");
          197         waitgs(g);
          198 
          199         return 0;
          200 }
          201 
          202 int
          203 gscmd(GSInfo *gs, char *fmt, ...)
          204 {
          205         char buf[1024];
          206         int n;
          207 
          208         va_list v;
          209         va_start(v, fmt);
          210         n = vseprint(buf, buf+sizeof buf, fmt, v) - buf;
          211         if(n <= 0)
          212                 return n;
          213 
          214         if(chatty) {
          215                 fprint(2, "cmd: ");
          216                 write(2, buf, n);
          217         }
          218 
          219         if(write(gs->gsfd, buf, n) != 0)
          220                 return -1;
          221 
          222         return n;
          223 }
          224 
          225 /*
          226  * set the dimensions of the bitmap we expect to get back from GS.
          227  */
          228 void
          229 setdim(GSInfo *gs, Rectangle bbox, int ppi, int landscape)
          230 {
          231         Rectangle pbox;
          232 
          233         if(chatty)
          234                 fprint(2, "setdim: bbox=%R\n", bbox);
          235 
          236         if(ppi)
          237                 gs->ppi = ppi;
          238 
          239         gscmd(gs, "mark\n");
          240         if(ppi)
          241                 gscmd(gs, "/HWResolution [%d %d]\n", ppi, ppi);
          242 
          243         if(!Dx(bbox))
          244                 bbox = Rect(0, 0, 612, 792);        /* 8½×11 */
          245 
          246         switch(landscape){
          247         case 0:
          248                 pbox = bbox;
          249                 break;
          250         default:
          251                 pbox = Rect(bbox.min.y, bbox.min.x, bbox.max.y, bbox.max.x);
          252                 break;
          253         }
          254         gscmd(gs, "/PageSize [%d %d]\n", Dx(pbox), Dy(pbox));
          255         gscmd(gs, "/Margins [%d %d]\n", -pbox.min.x, -pbox.min.y);
          256         gscmd(gs, "currentdevice putdeviceprops pop\n");
          257         gscmd(gs, "/#copies 1 store\n");
          258 
          259         if(!eqpt(bbox.min, ZP))
          260                 gscmd(gs, "%d %d translate\n", -bbox.min.x, -bbox.min.y);
          261 
          262         switch(landscape){
          263         case 0:
          264                 break;
          265         case 1:
          266                 gscmd(gs, "%d 0 translate\n", Dy(bbox));
          267                 gscmd(gs, "90 rotate\n");
          268                 break;
          269         }
          270 
          271         waitgs(gs);
          272 }
          273 
          274 void
          275 waitgs(GSInfo *gs)
          276 {
          277         /* we figure out that gs is done by telling it to
          278          * print something and waiting until it does.
          279          */
          280         char *p;
          281         Biobuf *b = &gs->gsrd;
          282         uchar buf[1024];
          283         int n;
          284 
          285 //        gscmd(gs, "(\\n**bstack\\n) print flush\n");
          286 //        gscmd(gs, "stack flush\n");
          287 //        gscmd(gs, "(**estack\\n) print flush\n");
          288         gscmd(gs, "(\\n//GO.SYSIN DD\\n) PAGE==\n");
          289 
          290         alarm(300*1000);
          291         for(;;) {
          292                 p = Brdline(b, '\n');
          293                 if(p == nil) {
          294                         n = Bbuffered(b);
          295                         if(n <= 0)
          296                                 break;
          297                         if(n > sizeof buf)
          298                                 n = sizeof buf;
          299                         Bread(b, buf, n);
          300                         continue;
          301                 }
          302                 p[Blinelen(b)-1] = 0;
          303                 if(chatty) fprint(2, "p: ");
          304                 if(chatty) write(2, p, Blinelen(b)-1);
          305                 if(chatty) fprint(2, "\n");
          306                 if(strstr(p, "Error:")) {
          307                         alarm(0);
          308                         fprint(2, "ghostscript error: %s\n", p);
          309                         wexits("gs error");
          310                 }
          311 
          312                 if(strstr(p, "//GO.SYSIN DD")) {
          313                         break;
          314                 }
          315         }
          316         alarm(0);
          317 }