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