1 | /* |
---|
2 | * Copyright (c) 1994 Paul Vojta. All rights reserved. |
---|
3 | * |
---|
4 | * Redistribution and use in source and binary forms, with or without |
---|
5 | * modification, are permitted provided that the following conditions |
---|
6 | * are met: |
---|
7 | * 1. Redistributions of source code must retain the above copyright |
---|
8 | * notice, this list of conditions and the following disclaimer. |
---|
9 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
10 | * notice, this list of conditions and the following disclaimer in the |
---|
11 | * documentation and/or other materials provided with the distribution. |
---|
12 | * |
---|
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
---|
14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
---|
17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
23 | * SUCH DAMAGE. |
---|
24 | */ |
---|
25 | |
---|
26 | #include "xdvi.h" |
---|
27 | #include <X11/Xatom.h> |
---|
28 | |
---|
29 | #include <memory.h> |
---|
30 | #include <signal.h> |
---|
31 | #include <sys/file.h> /* this defines FASYNC */ |
---|
32 | |
---|
33 | /* if POSIX O_NONBLOCK is not available, use O_NDELAY */ |
---|
34 | #if !defined(O_NONBLOCK) && defined(O_NDELAY) |
---|
35 | #define O_NONBLOCK O_NDELAY |
---|
36 | #endif |
---|
37 | |
---|
38 | /* Condition for retrying a write */ |
---|
39 | #include <errno.h> |
---|
40 | |
---|
41 | #ifdef X_NOT_STDC_ENV |
---|
42 | extern int errno; |
---|
43 | #endif |
---|
44 | |
---|
45 | #ifdef EWOULDBLOCK |
---|
46 | #ifdef EAGAIN |
---|
47 | #define AGAIN_CONDITION (errno == EWOULDBLOCK || errno == EAGAIN) |
---|
48 | #else /* EAGAIN */ |
---|
49 | #define AGAIN_CONDITION (errno == EWOULDBLOCK) |
---|
50 | #endif /* EAGAIN */ |
---|
51 | #else /* EWOULDBLOCK */ |
---|
52 | #ifdef EAGAIN |
---|
53 | #define AGAIN_CONDITION (errno == EAGAIN) |
---|
54 | #endif /* EAGAIN */ |
---|
55 | #endif /* EWOULDBLOCK */ |
---|
56 | |
---|
57 | #ifdef _POSIX_SOURCE |
---|
58 | #include <limits.h> |
---|
59 | #endif |
---|
60 | #ifndef PATH_MAX |
---|
61 | #define PATH_MAX 512 |
---|
62 | #endif |
---|
63 | |
---|
64 | #ifdef STREAMSCONN |
---|
65 | #include <poll.h> |
---|
66 | #endif |
---|
67 | |
---|
68 | #if HAS_SIGIO && !defined(STRMS2) |
---|
69 | #include <signal.h> |
---|
70 | #ifndef FASYNC |
---|
71 | #undef HAS_SIGIO |
---|
72 | #define HAS_SIGIO 0 |
---|
73 | #endif |
---|
74 | #endif |
---|
75 | |
---|
76 | #ifdef VFORK |
---|
77 | #if VFORK == include |
---|
78 | #include <vfork.h> |
---|
79 | #endif |
---|
80 | #else |
---|
81 | #define vfork fork |
---|
82 | #endif |
---|
83 | |
---|
84 | #ifndef X_GETTIMEOFDAY |
---|
85 | #define X_GETTIMEOFDAY(t) gettimeofday(t, (struct timezone*) NULL) |
---|
86 | #endif |
---|
87 | |
---|
88 | #ifndef timercmp |
---|
89 | #define timercmp(a, b, cmp) ((a)->tv_sec cmp (b)->tv_sec || \ |
---|
90 | ((a)->tv_sec == (b)->tv_sec && (a)->tv_usec cmp (b)->tv_usec)) |
---|
91 | #endif /* timercmp */ |
---|
92 | |
---|
93 | extern _Xconst char psheader[]; |
---|
94 | extern int psheaderlen; |
---|
95 | |
---|
96 | #define postscript resource._postscript |
---|
97 | #define fore_Pixel resource._fore_Pixel |
---|
98 | #define back_Pixel resource._back_Pixel |
---|
99 | |
---|
100 | /* global procedures (besides initGS) */ |
---|
101 | |
---|
102 | static void toggle_gs ARGS((void)); |
---|
103 | static void destroy_gs ARGS((void)); |
---|
104 | static void interrupt_gs ARGS((void)); |
---|
105 | static void endpage_gs ARGS((void)); |
---|
106 | static void drawbegin_gs ARGS((int, int, _Xconst char *)); |
---|
107 | static void drawraw_gs ARGS((_Xconst char *)); |
---|
108 | static void drawfile_gs ARGS((_Xconst char *, FILE *)); |
---|
109 | static void drawend_gs ARGS((_Xconst char *)); |
---|
110 | static void beginheader_gs ARGS((void)); |
---|
111 | static void endheader_gs ARGS((void)); |
---|
112 | static void newdoc_gs ARGS((void)); |
---|
113 | |
---|
114 | static struct psprocs gs_procs = { |
---|
115 | /* toggle */ toggle_gs, |
---|
116 | /* destroy */ destroy_gs, |
---|
117 | /* interrupt */ interrupt_gs, |
---|
118 | /* endpage */ endpage_gs, |
---|
119 | /* drawbegin */ drawbegin_gs, |
---|
120 | /* drawraw */ drawraw_gs, |
---|
121 | /* drawfile */ drawfile_gs, |
---|
122 | /* drawend */ drawend_gs, |
---|
123 | /* beginheader */ beginheader_gs, |
---|
124 | /* endheader */ endheader_gs, |
---|
125 | /* newdoc */ newdoc_gs}; |
---|
126 | |
---|
127 | static int std_in[2]; |
---|
128 | static int std_out[2]; |
---|
129 | |
---|
130 | #define GS_in (std_in[1]) |
---|
131 | #define GS_out (std_out[0]) |
---|
132 | |
---|
133 | /* some arguments are filled in later */ |
---|
134 | static char arg4[] = "-dDEVICEWIDTH=xxxxxxxxxx"; |
---|
135 | static char arg5[] = "-dDEVICEHEIGHT=xxxxxxxxxx"; |
---|
136 | |
---|
137 | static _Xconst char *argv[] = {NULL, "-sDEVICE=x11", "-dNOPAUSE", "-q", |
---|
138 | arg4, arg5, |
---|
139 | "-dDEVICEXRESOLUTION=72", |
---|
140 | "-dDEVICEYRESOLUTION=72", |
---|
141 | NULL, NULL, NULL}; |
---|
142 | |
---|
143 | static pid_t GS_pid; |
---|
144 | static unsigned int GS_page_w; /* how big our current page is */ |
---|
145 | static unsigned int GS_page_h; |
---|
146 | static int GS_mag; /* magnification currently in use */ |
---|
147 | static int GS_shrink; /* shrink factor currently in use */ |
---|
148 | static Boolean GS_active; /* if we've started a page yet */ |
---|
149 | static int GS_pending; /* number of ack's we're expecting */ |
---|
150 | static _Xconst char *GS_send_byte; /* next byte to send to gs */ |
---|
151 | static _Xconst char *GS_send_end; /* last + 1 byte to send to gs */ |
---|
152 | static Boolean GS_in_header; /* if we're sending a header */ |
---|
153 | static Boolean GS_in_doc; /* if we've sent header information */ |
---|
154 | static Boolean GS_pending_int; /* if interrupt rec'd while in gs_io()*/ |
---|
155 | static Boolean GS_timeout_set; /* if there's a timeout set */ |
---|
156 | static struct timeval GS_timeout; /* when to time out */ |
---|
157 | static Boolean GS_old; /* if we're using gs 2.xx */ |
---|
158 | |
---|
159 | static Atom gs_atom; |
---|
160 | static Atom gs_colors_atom; |
---|
161 | |
---|
162 | #define Landscape 90 |
---|
163 | |
---|
164 | /* |
---|
165 | * Our replacement for setenv(), which is not available on all systems. |
---|
166 | */ |
---|
167 | |
---|
168 | #ifndef HAVE_SETENV /* define this if you're a performance freak and |
---|
169 | if your system has setenv. */ |
---|
170 | #define setenv(var, str, repl) _setenv(var, str) /* repl always True */ |
---|
171 | |
---|
172 | extern char **environ; |
---|
173 | |
---|
174 | static void |
---|
175 | _setenv(var, str) |
---|
176 | _Xconst char *var; |
---|
177 | _Xconst char *str; |
---|
178 | { |
---|
179 | int len1; |
---|
180 | int len2; |
---|
181 | char *newvar; |
---|
182 | char **linep; |
---|
183 | static Boolean malloced = False; |
---|
184 | |
---|
185 | len1 = strlen(var); |
---|
186 | len2 = strlen(str) + 1; |
---|
187 | newvar = xmalloc((unsigned int) len1 + len2 + 1, "_setenv"); |
---|
188 | (void) bcopy(var, newvar, len1); |
---|
189 | newvar[len1++] = '='; |
---|
190 | (void) bcopy(str, newvar + len1, len2); |
---|
191 | for (linep = environ; *linep != NULL; ++linep) |
---|
192 | if (memcmp(*linep, newvar, len1) == 0) { |
---|
193 | *linep = newvar; |
---|
194 | return; |
---|
195 | } |
---|
196 | len1 = linep - environ; |
---|
197 | if (malloced) |
---|
198 | environ = (char **) xrealloc((char *) environ, |
---|
199 | (unsigned int) (len1 + 2) * sizeof(char *), |
---|
200 | "string list in _setenv"); |
---|
201 | else { |
---|
202 | linep = (char **) xmalloc((unsigned int)(len1 + 2) * sizeof(char *), |
---|
203 | "string list in _setenv"); |
---|
204 | (void) bcopy((char *) environ, (char *) linep, |
---|
205 | len1 * sizeof(char *)); |
---|
206 | environ = linep; |
---|
207 | malloced = True; |
---|
208 | } |
---|
209 | environ[len1] = newvar; |
---|
210 | environ[len1 + 1] = NULL; |
---|
211 | } |
---|
212 | |
---|
213 | #endif /* HAVE_SETENV */ |
---|
214 | |
---|
215 | /* |
---|
216 | * ghostscript I/O code. This should send PS code to ghostscript, |
---|
217 | * receive acknowledgements, and receive X events in the meantime. |
---|
218 | * It also checks for SIGPIPE errors. |
---|
219 | */ |
---|
220 | |
---|
221 | #ifndef STREAMSCONN |
---|
222 | static int numfds; |
---|
223 | static fd_set readfds; |
---|
224 | static fd_set writefds; |
---|
225 | #define XDVI_ISSET(a, b, c) FD_ISSET(a, b) |
---|
226 | #else /* STREAMSCONN */ |
---|
227 | static struct pollfd fds[3] = {{0, POLLOUT, 0}, |
---|
228 | {0, POLLIN, 0}, |
---|
229 | {0, POLLIN, 0}}; |
---|
230 | #define XDVI_ISSET(a, b, c) (fds[c].revents) |
---|
231 | #endif /* STREAMSCONN */ |
---|
232 | |
---|
233 | #define LINELEN 81 |
---|
234 | static char line[LINELEN + 1]; |
---|
235 | static char *linepos = line; |
---|
236 | static char ackstr[] = "\347\310\376"; |
---|
237 | static char oldstr[] = "\347\310\375"; |
---|
238 | |
---|
239 | static void |
---|
240 | showto(q) |
---|
241 | char *q; |
---|
242 | { |
---|
243 | char *p = line; |
---|
244 | char *p1; |
---|
245 | |
---|
246 | while (p < q) { |
---|
247 | p1 = memchr(p, '\n', q - p); |
---|
248 | if (p1 == NULL) p1 = q; |
---|
249 | *p1 = '\0'; |
---|
250 | Printf("gs: %s\n", p); |
---|
251 | p = p1 + 1; |
---|
252 | } |
---|
253 | } |
---|
254 | |
---|
255 | static void |
---|
256 | read_from_gs() { |
---|
257 | int bytes; |
---|
258 | char *line_end; |
---|
259 | char *p; |
---|
260 | |
---|
261 | bytes = read(GS_out, linepos, line + LINELEN - linepos); |
---|
262 | if (bytes < 0) return; |
---|
263 | line_end = linepos + bytes; |
---|
264 | /* Check for ack strings */ |
---|
265 | for (p = line; p < line_end - 2; ++p) { |
---|
266 | p = memchr(p, '\347', line_end - p - 2); |
---|
267 | if (p == NULL) break; |
---|
268 | if (memcmp(p, ackstr, 3) == 0) { |
---|
269 | --GS_pending; |
---|
270 | if (debug & DBG_PS) |
---|
271 | Printf("Got GS ack; %d pending.\n", GS_pending); |
---|
272 | } |
---|
273 | else if (memcmp(p, oldstr, 3) == 0) { |
---|
274 | if (debug & DBG_PS) |
---|
275 | Puts("Using old GS version."); |
---|
276 | GS_old = True; |
---|
277 | } |
---|
278 | else continue; |
---|
279 | |
---|
280 | showto(p); |
---|
281 | p += 3; |
---|
282 | (void) bcopy(p, line, line_end - p); |
---|
283 | line_end -= p - line; |
---|
284 | linepos = p = line; |
---|
285 | --p; |
---|
286 | } |
---|
287 | *line_end = '\0'; |
---|
288 | p = rindex(linepos, '\n'); |
---|
289 | if (p != NULL) { |
---|
290 | ++p; |
---|
291 | showto(p); |
---|
292 | (void) bcopy(p, line, line_end - p); |
---|
293 | line_end -= p - line; |
---|
294 | } |
---|
295 | linepos = line_end; |
---|
296 | /* |
---|
297 | * Normally we'd hold text until a newline character, but the buffer |
---|
298 | * is full. So we flush it, being careful not to cut up an ack string. |
---|
299 | */ |
---|
300 | if (linepos >= line + LINELEN) { |
---|
301 | p = line + LINELEN; |
---|
302 | if ((*--p != '\347' && *--p != '\347' && *--p != '\347') |
---|
303 | || (memcmp(p, ackstr, line + LINELEN - p) != 0 |
---|
304 | && memcmp(p, oldstr, line + LINELEN - p) != 0)) |
---|
305 | p = line + LINELEN; |
---|
306 | *p = '\0'; |
---|
307 | Printf("gs: %s\n", line); |
---|
308 | *p = '\347'; |
---|
309 | linepos = line; |
---|
310 | while (p < line + LINELEN) *linepos++ = *p++; |
---|
311 | } |
---|
312 | } |
---|
313 | |
---|
314 | /* |
---|
315 | * For handling of SIGPIPE signals from gs_io() |
---|
316 | */ |
---|
317 | |
---|
318 | static Boolean sigpipe_error = False; |
---|
319 | |
---|
320 | /* ARGSUSED */ |
---|
321 | static void |
---|
322 | gs_sigpipe_handler(sig, code, scp, addr) |
---|
323 | int sig; |
---|
324 | int code; |
---|
325 | struct sigcontext *scp; |
---|
326 | char *addr; |
---|
327 | { |
---|
328 | sigpipe_error = True; |
---|
329 | } |
---|
330 | |
---|
331 | #ifdef _POSIX_SOURCE |
---|
332 | static struct sigaction sigpipe_handler_struct; |
---|
333 | /* initialized to {gs_sigpipe_handler, (sigset_t) 0, 0} in initGS */ |
---|
334 | #endif |
---|
335 | |
---|
336 | /* |
---|
337 | * Clean up after gs_io() |
---|
338 | */ |
---|
339 | |
---|
340 | static void |
---|
341 | post_send() |
---|
342 | { |
---|
343 | if (sigpipe_error) { |
---|
344 | Fputs("ghostscript died unexpectedly.\n", stderr); |
---|
345 | destroy_gs(); |
---|
346 | draw_bbox(); |
---|
347 | } |
---|
348 | |
---|
349 | if (GS_pending_int) { |
---|
350 | GS_pending_int = False; |
---|
351 | interrupt_gs(); |
---|
352 | } |
---|
353 | } |
---|
354 | |
---|
355 | /* |
---|
356 | * This routine does two things. It either sends a string of bytes to |
---|
357 | * the GS interpreter, or waits for acknowledgement from GS. |
---|
358 | */ |
---|
359 | |
---|
360 | #define waitack() gs_io(NULL, 0) |
---|
361 | |
---|
362 | static void |
---|
363 | gs_io(cp, len) |
---|
364 | _Xconst char *cp; |
---|
365 | int len; |
---|
366 | { |
---|
367 | int bytes; |
---|
368 | #ifdef _POSIX_SOURCE |
---|
369 | struct sigaction orig; |
---|
370 | #else |
---|
371 | void (*orig)(); |
---|
372 | #endif |
---|
373 | struct timeval tv; |
---|
374 | #ifndef STREAMSCONN |
---|
375 | struct timeval *timeout = (struct timeval *) NULL; |
---|
376 | #else |
---|
377 | int timeout = -1; |
---|
378 | int retval; |
---|
379 | int offset = 0; |
---|
380 | #endif |
---|
381 | #if HAS_SIGIO && defined(FASYNC) |
---|
382 | int oldflags; |
---|
383 | #endif |
---|
384 | |
---|
385 | if (GS_pid < 0) |
---|
386 | return; |
---|
387 | |
---|
388 | if (cp != NULL) { /* if sending bytes */ |
---|
389 | #ifdef _POSIX_SOURCE |
---|
390 | (void) sigaction(SIGPIPE, &sigpipe_handler_struct, &orig); |
---|
391 | #else |
---|
392 | orig = signal(SIGPIPE, gs_sigpipe_handler); |
---|
393 | #endif |
---|
394 | sigpipe_error = False; |
---|
395 | GS_send_byte = cp; |
---|
396 | GS_send_end = cp + len; |
---|
397 | } |
---|
398 | else { /* if waiting for acknowledgement */ |
---|
399 | if (GS_pending == 0) |
---|
400 | return; |
---|
401 | #ifdef STREAMSCONN |
---|
402 | offset = 1; |
---|
403 | #endif |
---|
404 | } |
---|
405 | |
---|
406 | #if HAS_SIGIO && defined(FASYNC) |
---|
407 | oldflags = fcntl(ConnectionNumber(DISP), F_GETFL, 0); |
---|
408 | (void) fcntl(ConnectionNumber(DISP), F_SETFL, oldflags & ~FASYNC); |
---|
409 | #endif |
---|
410 | |
---|
411 | for (;;) { |
---|
412 | |
---|
413 | /* Handle timeout. */ |
---|
414 | |
---|
415 | if (GS_timeout_set) { |
---|
416 | (void) X_GETTIMEOFDAY(&tv); |
---|
417 | #ifndef STREAMSCONN |
---|
418 | if (!timercmp(&tv, &GS_timeout, <)) { |
---|
419 | destroy_gs(); |
---|
420 | break; |
---|
421 | } |
---|
422 | tv.tv_sec = GS_timeout.tv_sec - tv.tv_sec; |
---|
423 | tv.tv_usec = GS_timeout.tv_usec + 1000000 - tv.tv_usec; |
---|
424 | if (tv.tv_usec >= 1000000) tv.tv_usec -= 1000000; |
---|
425 | else --tv.tv_sec; |
---|
426 | timeout = &tv; |
---|
427 | #else |
---|
428 | timeout = 1000 * (int) (GS_timeout.tv_sec - tv.tv_sec) |
---|
429 | + ((long) GS_timeout.tv_usec - (long) tv.tv_usec) / 1000; |
---|
430 | if (timeout <= 0) { |
---|
431 | destroy_gs(); |
---|
432 | break; |
---|
433 | } |
---|
434 | #endif |
---|
435 | } |
---|
436 | |
---|
437 | #ifndef STREAMSCONN |
---|
438 | FD_ZERO(&readfds); |
---|
439 | FD_SET(ConnectionNumber(DISP), &readfds); |
---|
440 | FD_SET(GS_out, &readfds); |
---|
441 | |
---|
442 | FD_ZERO(&writefds); |
---|
443 | if (GS_send_byte != NULL) FD_SET(GS_in, &writefds); |
---|
444 | |
---|
445 | if (select(numfds, &readfds, &writefds, (fd_set *) NULL, timeout) |
---|
446 | < 0 && errno != EINTR) { |
---|
447 | perror("select (xdvi gs_io)"); |
---|
448 | break; |
---|
449 | } |
---|
450 | #else /* STREAMSCONN */ |
---|
451 | fds[0].revents = 0; |
---|
452 | for (;;) { |
---|
453 | retval = poll(fds + offset, XtNumber(fds) - offset, timeout); |
---|
454 | if (retval >= 0 || errno != EAGAIN) break; |
---|
455 | } |
---|
456 | if (retval < 0) { |
---|
457 | perror("poll (xdvi gs_io)"); |
---|
458 | break; |
---|
459 | } |
---|
460 | #endif /* STREAMSCONN */ |
---|
461 | |
---|
462 | if (XDVI_ISSET(GS_out, &readfds, 1)) { |
---|
463 | read_from_gs(); |
---|
464 | if (GS_pending <= 0) { |
---|
465 | GS_timeout_set = False; |
---|
466 | break; |
---|
467 | } |
---|
468 | } |
---|
469 | if (XDVI_ISSET(GS_in, &writefds, 0)) { |
---|
470 | bytes = write(GS_in, GS_send_byte, GS_send_end - GS_send_byte); |
---|
471 | if (bytes == -1) { |
---|
472 | if (!AGAIN_CONDITION) perror("xdvi gs_io"); |
---|
473 | } |
---|
474 | else { |
---|
475 | GS_send_byte += bytes; |
---|
476 | if (GS_send_byte == GS_send_end) { |
---|
477 | GS_send_byte = NULL; |
---|
478 | break; |
---|
479 | } |
---|
480 | } |
---|
481 | if (sigpipe_error) break; |
---|
482 | } |
---|
483 | if (XDVI_ISSET(ConnectionNumber(DISP), &readfds, 2)) { |
---|
484 | ps_read_events(False, False); |
---|
485 | if (GS_pid < 0) break; /* if timeout occurred */ |
---|
486 | if (GS_pending <= 0) { |
---|
487 | GS_timeout_set = False; |
---|
488 | break; |
---|
489 | } |
---|
490 | } |
---|
491 | } |
---|
492 | |
---|
493 | #if HAS_SIGIO && defined(FASYNC) |
---|
494 | (void) fcntl(ConnectionNumber(DISP), F_SETFL, oldflags); |
---|
495 | #endif |
---|
496 | |
---|
497 | if (cp != NULL) { |
---|
498 | /* put back generic handler for SIGPIPE */ |
---|
499 | #ifdef _POSIX_SOURCE |
---|
500 | (void) sigaction(SIGPIPE, &orig, (struct sigaction *) NULL); |
---|
501 | #else |
---|
502 | (void) signal(SIGPIPE, orig); |
---|
503 | #endif |
---|
504 | if (!GS_in_header) |
---|
505 | post_send(); |
---|
506 | } |
---|
507 | } |
---|
508 | |
---|
509 | /* |
---|
510 | * Fork a process to run ghostscript. This is done using the |
---|
511 | * x11 device (which needs to be compiled in). Normally the x11 |
---|
512 | * device uses ClientMessage events to communicate with the calling |
---|
513 | * program, but we don't do this. The reason for using the ClientMessage |
---|
514 | * events is that otherwise ghostview doesn't know when a non-conforming |
---|
515 | * postscript program calls showpage. That doesn't affect us here, |
---|
516 | * since in fact we disable showpage. |
---|
517 | */ |
---|
518 | |
---|
519 | Boolean |
---|
520 | initGS() |
---|
521 | { |
---|
522 | char buf[100]; |
---|
523 | /* |
---|
524 | * This string reads chunks (delimited by %%xdvimark). |
---|
525 | * The first character of a chunk tells whether a given chunk |
---|
526 | * is to be done within save/restore or not. |
---|
527 | * The `H' at the end tells it that the first group is a |
---|
528 | * header; i.e., no save/restore. |
---|
529 | * `execute' is unique to ghostscript. |
---|
530 | */ |
---|
531 | static _Xconst char str1[] = "\ |
---|
532 | /xdvi$run {$error /newerror false put {currentfile cvx execute} stopped pop} \ |
---|
533 | def \ |
---|
534 | /xdvi$ack (\347\310\376) def \ |
---|
535 | /xdvi$dslen countdictstack def \ |
---|
536 | {currentfile read pop 72 eq \ |
---|
537 | {xdvi$run} \ |
---|
538 | {/xdvi$sav save def xdvi$run \ |
---|
539 | clear countdictstack xdvi$dslen sub {end} repeat xdvi$sav restore} \ |
---|
540 | ifelse \ |
---|
541 | {(%%xdvimark) currentfile =string {readline} stopped \ |
---|
542 | {clear} {pop eq {exit} if} ifelse }loop \ |
---|
543 | flushpage xdvi$ack print flush \ |
---|
544 | }loop\nH"; |
---|
545 | static _Xconst char str2[] = "[0 1 1 0 0 0] concat\n\ |
---|
546 | revision 300 lt{(\347\310\375) print flush}if\n\ |
---|
547 | stop\n%%xdvimark\n"; |
---|
548 | |
---|
549 | gs_atom = XInternAtom(DISP, "GHOSTVIEW", False); |
---|
550 | /* send bpixmap, orientation, bbox (in pixels), and h & v resolution */ |
---|
551 | Sprintf(buf, "%ld %d 0 0 %u %u 72 72", |
---|
552 | None, /* bpixmap */ |
---|
553 | Landscape, /* orientation */ |
---|
554 | GS_page_h = page_h, GS_page_w = page_w); |
---|
555 | XChangeProperty(DISP, mane.win, gs_atom, XA_STRING, 8, |
---|
556 | PropModeReplace, (unsigned char *) buf, strlen(buf)); |
---|
557 | |
---|
558 | gs_colors_atom = XInternAtom(DISP, "GHOSTVIEW_COLORS", False); |
---|
559 | Sprintf(buf, "%s %ld %ld", resource.gs_palette, fore_Pixel, back_Pixel); |
---|
560 | XChangeProperty(DISP, mane.win, gs_colors_atom, XA_STRING, 8, |
---|
561 | PropModeReplace, (unsigned char *) buf, strlen(buf)); |
---|
562 | |
---|
563 | XSync(DISP, False); /* update the window */ |
---|
564 | |
---|
565 | if (xpipe(std_in) != 0 || xpipe(std_out) != 0) { |
---|
566 | perror("[xdvi] pipe"); |
---|
567 | return False; |
---|
568 | } |
---|
569 | Fflush(stderr); /* to avoid double flushing */ |
---|
570 | GS_pid = vfork(); |
---|
571 | if (GS_pid == 0) { /* child */ |
---|
572 | _Xconst char **argvp = argv + 8; |
---|
573 | |
---|
574 | Sprintf(arg4 + 14, "%u", GS_page_w); |
---|
575 | Sprintf(arg5 + 15, "%u", GS_page_h); |
---|
576 | if (resource.gs_safer) *argvp++ = "-dSAFER"; |
---|
577 | *argvp = "-"; |
---|
578 | Sprintf(buf, "%ld", mane.win); |
---|
579 | setenv("GHOSTVIEW", buf, True); |
---|
580 | setenv("DISPLAY", XDisplayString(DISP), True); |
---|
581 | (void) close(std_in[1]); |
---|
582 | (void) dup2(std_in[0], 0); |
---|
583 | (void) close(std_in[0]); |
---|
584 | (void) close(std_out[0]); |
---|
585 | (void) dup2(std_out[1], 1); |
---|
586 | (void) dup2(std_out[1], 2); |
---|
587 | (void) close(std_out[1]); |
---|
588 | (void) execvp(argv[0] = resource.gs_path, (char * _Xconst *) argv); |
---|
589 | Fprintf(stderr, "Execvp of %s failed.\n", argv[0]); |
---|
590 | Fflush(stderr); |
---|
591 | _exit(1); |
---|
592 | } |
---|
593 | if (GS_pid == -1) { /* error */ |
---|
594 | perror("[xdvi] vfork"); |
---|
595 | return False; |
---|
596 | } |
---|
597 | (void) close(std_in[0]); |
---|
598 | (void) close(std_out[1]); |
---|
599 | |
---|
600 | /* Set std_in for non-blocking I/O */ |
---|
601 | (void) fcntl(std_in[1], F_SETFL, |
---|
602 | fcntl(std_in[1], F_GETFL, 0) | O_NONBLOCK); |
---|
603 | |
---|
604 | #ifdef _POSIX_SOURCE |
---|
605 | sigpipe_handler_struct.sa_handler = gs_sigpipe_handler; |
---|
606 | sigemptyset(&sigpipe_handler_struct.sa_mask); |
---|
607 | #endif |
---|
608 | |
---|
609 | #ifndef STREAMSCONN |
---|
610 | numfds = ConnectionNumber(DISP); |
---|
611 | if (numfds < std_in[1]) numfds = std_in[1]; |
---|
612 | if (numfds < std_out[0]) numfds = std_out[0]; |
---|
613 | ++numfds; |
---|
614 | #else /* STREAMSCONN */ |
---|
615 | fds[0].fd = std_in[1]; |
---|
616 | fds[1].fd = std_out[0]; |
---|
617 | fds[2].fd = ConnectionNumber(DISP); |
---|
618 | #endif /* STREAMSCONN */ |
---|
619 | |
---|
620 | psp = gs_procs; |
---|
621 | GS_active = GS_pending_int = GS_timeout_set = False; |
---|
622 | GS_send_byte = NULL; |
---|
623 | GS_in_header = True; |
---|
624 | GS_pending = 1; |
---|
625 | GS_mag = GS_shrink = -1; |
---|
626 | |
---|
627 | gs_io(str1, sizeof(str1) - 1); |
---|
628 | gs_io(psheader, psheaderlen); |
---|
629 | gs_io(str2, sizeof(str2) - 1); |
---|
630 | GS_in_header = False; |
---|
631 | post_send(); |
---|
632 | waitack(); |
---|
633 | |
---|
634 | if (GS_pid < 0) { /* if something happened */ |
---|
635 | destroy_gs(); |
---|
636 | return False; |
---|
637 | } |
---|
638 | if (!postscript) toggle_gs(); /* if we got a 'v' already */ |
---|
639 | else { |
---|
640 | canit = True; /* ||| redraw the page */ |
---|
641 | scanned_page = scanned_page_bak = scanned_page_reset; |
---|
642 | longjmp(canit_env, 1); |
---|
643 | } |
---|
644 | return True; |
---|
645 | } |
---|
646 | |
---|
647 | static void |
---|
648 | toggle_gs() |
---|
649 | { |
---|
650 | if (debug & DBG_PS) Puts("Toggling GS on or off"); |
---|
651 | if (postscript) psp.drawbegin = drawbegin_gs; |
---|
652 | else { |
---|
653 | interrupt_gs(); |
---|
654 | psp.drawbegin = drawbegin_none; |
---|
655 | } |
---|
656 | } |
---|
657 | |
---|
658 | static void |
---|
659 | destroy_gs() |
---|
660 | { |
---|
661 | if (debug & DBG_PS) Puts("Destroying GS process"); |
---|
662 | if (linepos > line) { |
---|
663 | *linepos = '\0'; |
---|
664 | Printf("gs: %s\n", line); |
---|
665 | linepos = line; |
---|
666 | } |
---|
667 | if (GS_pid >= 0) { |
---|
668 | if (kill(GS_pid, SIGKILL) < 0 && errno != ESRCH) |
---|
669 | perror("xdvi destroy_gs"); |
---|
670 | GS_pid = -1; |
---|
671 | scanned_page = scanned_page_bak = scanned_page_reset; |
---|
672 | } |
---|
673 | (void) close(GS_in); |
---|
674 | (void) close(GS_out); |
---|
675 | GS_active = GS_pending_int = GS_timeout_set = GS_in_doc = False; |
---|
676 | GS_send_byte = NULL; |
---|
677 | GS_pending = 0; |
---|
678 | } |
---|
679 | |
---|
680 | static void |
---|
681 | interrupt_gs() |
---|
682 | { |
---|
683 | static _Xconst char str[] = " stop\n%%xdvimark\n"; |
---|
684 | |
---|
685 | if (debug & DBG_PS) Puts("Running interrupt_gs()"); |
---|
686 | if (GS_pending <= 0) return; /* nothing to do */ |
---|
687 | if (GS_timeout_set) return; /* we've done this already */ |
---|
688 | (void) X_GETTIMEOFDAY(&GS_timeout); /* set timeout */ |
---|
689 | GS_timeout.tv_sec += 5; |
---|
690 | GS_timeout_set = True; |
---|
691 | if (GS_send_byte != NULL) GS_pending_int = True; |
---|
692 | else { |
---|
693 | if (GS_active) { |
---|
694 | /* |
---|
695 | * ||| what I'd really like to do here is cause gs to execute |
---|
696 | * the interrupt routine in errordict. But so far (gs 2.6.1) |
---|
697 | * that has not been implemented in ghostscript. |
---|
698 | */ |
---|
699 | gs_io(str, sizeof(str) - 1); |
---|
700 | GS_active = False; |
---|
701 | } |
---|
702 | psp.interrupt = NullProc; /* prevent deep recursion in waitack */ |
---|
703 | waitack(); |
---|
704 | psp.interrupt = interrupt_gs; |
---|
705 | } |
---|
706 | } |
---|
707 | |
---|
708 | static void |
---|
709 | endpage_gs() |
---|
710 | { |
---|
711 | static _Xconst char str[] = "stop\n%%xdvimark\n"; |
---|
712 | |
---|
713 | if (debug & DBG_PS) Puts("Running endpage_gs()"); |
---|
714 | if (GS_active) { |
---|
715 | gs_io(str, sizeof(str) - 1); |
---|
716 | GS_active = False; |
---|
717 | waitack(); |
---|
718 | } |
---|
719 | } |
---|
720 | |
---|
721 | /* |
---|
722 | * Checks that the GS interpreter is running correctly. |
---|
723 | */ |
---|
724 | |
---|
725 | static void |
---|
726 | checkgs(in_header) |
---|
727 | Boolean in_header; |
---|
728 | { |
---|
729 | char buf[150]; |
---|
730 | |
---|
731 | /* For gs 2, we pretty much have to start over to enlarge the window. */ |
---|
732 | if (GS_old && (page_w > GS_page_w || page_h > GS_page_h)) |
---|
733 | destroy_gs(); |
---|
734 | |
---|
735 | if (GS_pid < 0) |
---|
736 | (void) initGS(); |
---|
737 | |
---|
738 | if (!GS_active) { |
---|
739 | /* check whether page_w or page_h have increased */ |
---|
740 | if (page_w > GS_page_w || page_h > GS_page_h) { |
---|
741 | ++GS_pending; |
---|
742 | Sprintf(buf, "H mark /HWSize [%d %d] /ImagingBBox [0 0 %d %d] \ |
---|
743 | currentdevice putdeviceprops pop\n\ |
---|
744 | initgraphics [0 1 1 0 0 0] concat stop\n%%%%xdvimark\n", |
---|
745 | GS_page_w = page_w, GS_page_h = page_h, page_h, page_w); |
---|
746 | gs_io(buf, strlen(buf)); |
---|
747 | if (!in_header) { |
---|
748 | canit = True; /* ||| redraw the page */ |
---|
749 | longjmp(canit_env, 1); |
---|
750 | } |
---|
751 | } |
---|
752 | |
---|
753 | if (magnification != GS_mag) { |
---|
754 | ++GS_pending; |
---|
755 | Sprintf(buf, "H TeXDict begin /DVImag %d 1000 div def \ |
---|
756 | end stop\n%%%%xdvimark\n", |
---|
757 | GS_mag = magnification); |
---|
758 | gs_io(buf, strlen(buf)); |
---|
759 | } |
---|
760 | |
---|
761 | if (mane.shrinkfactor != GS_shrink) { |
---|
762 | ++GS_pending; |
---|
763 | Sprintf(buf, |
---|
764 | "H TeXDict begin %d %d div dup \ |
---|
765 | /Resolution X /VResolution X \ |
---|
766 | end stop\n%%%%xdvimark\n", |
---|
767 | pixels_per_inch, GS_shrink = mane.shrinkfactor); |
---|
768 | gs_io(buf, strlen(buf)); |
---|
769 | } |
---|
770 | } |
---|
771 | } |
---|
772 | |
---|
773 | static void |
---|
774 | drawbegin_gs(xul, yul, cp) |
---|
775 | int xul, yul; |
---|
776 | _Xconst char *cp; |
---|
777 | { |
---|
778 | char buf[32]; |
---|
779 | static _Xconst char str[] = " TeXDict begin\n"; |
---|
780 | |
---|
781 | checkgs(False); |
---|
782 | |
---|
783 | if (!GS_active) { |
---|
784 | ++GS_pending; |
---|
785 | gs_io(str, sizeof(str) - 1); |
---|
786 | GS_active = True; |
---|
787 | } |
---|
788 | |
---|
789 | /* This allows the X side to clear the page */ |
---|
790 | XSync(DISP, False); |
---|
791 | |
---|
792 | Sprintf(buf, "%d %d moveto\n", xul, yul); |
---|
793 | gs_io(buf, strlen(buf)); |
---|
794 | if (debug & DBG_PS) |
---|
795 | Printf("drawbegin at %d,%d: sending `%s'\n", xul, yul, cp); |
---|
796 | gs_io(cp, strlen(cp)); |
---|
797 | } |
---|
798 | |
---|
799 | static void |
---|
800 | drawraw_gs(cp) |
---|
801 | _Xconst char *cp; |
---|
802 | { |
---|
803 | if (!GS_active) |
---|
804 | return; |
---|
805 | if (debug & DBG_PS) Printf("raw ps sent to context: %s\n", cp); |
---|
806 | gs_io(cp, strlen(cp)); |
---|
807 | gs_io("\n", 1); |
---|
808 | } |
---|
809 | |
---|
810 | static void |
---|
811 | drawfile_gs(cp, f) |
---|
812 | _Xconst char *cp; |
---|
813 | FILE *f; |
---|
814 | { |
---|
815 | char buf[PATH_MAX + 7]; |
---|
816 | |
---|
817 | Fclose(f); /* don't need it */ |
---|
818 | ++n_files_left; |
---|
819 | |
---|
820 | if (!GS_active) |
---|
821 | return; |
---|
822 | |
---|
823 | if (debug & DBG_PS) Printf("printing file %s\n", cp); |
---|
824 | Sprintf(buf, "(%s)run\n", cp); |
---|
825 | gs_io(buf, strlen(buf)); |
---|
826 | } |
---|
827 | |
---|
828 | static void |
---|
829 | drawend_gs(cp) |
---|
830 | _Xconst char *cp; |
---|
831 | { |
---|
832 | if (!GS_active) |
---|
833 | return; |
---|
834 | if (debug & DBG_PS) Printf("end ps: %s\n", cp); |
---|
835 | gs_io(cp, strlen(cp)); |
---|
836 | gs_io("\n", 1); |
---|
837 | } |
---|
838 | |
---|
839 | static void |
---|
840 | beginheader_gs() |
---|
841 | { |
---|
842 | static _Xconst char str[] = "Hsave /xdvi$doc exch def\n"; |
---|
843 | |
---|
844 | if (debug & DBG_PS) Puts("Running beginheader_gs()"); |
---|
845 | |
---|
846 | checkgs(True); |
---|
847 | |
---|
848 | if (GS_active) { |
---|
849 | if (!GS_in_header) |
---|
850 | oops("Internal error in beginheader_gs().\n"); |
---|
851 | return; |
---|
852 | } |
---|
853 | |
---|
854 | GS_in_header = True; |
---|
855 | ++GS_pending; |
---|
856 | if (GS_in_doc) |
---|
857 | gs_io("H", 1); |
---|
858 | else { |
---|
859 | gs_io(str, sizeof(str) - 1); |
---|
860 | GS_in_doc = True; |
---|
861 | } |
---|
862 | GS_active = True; |
---|
863 | } |
---|
864 | |
---|
865 | static void |
---|
866 | endheader_gs() |
---|
867 | { |
---|
868 | static _Xconst char str[] = "stop\n%%xdvimark\n"; |
---|
869 | |
---|
870 | if (debug & DBG_PS) Puts("Running endheader_gs()"); |
---|
871 | |
---|
872 | if (GS_active) { |
---|
873 | gs_io(str, sizeof(str) - 1); |
---|
874 | GS_active = False; |
---|
875 | GS_in_header = False; |
---|
876 | post_send(); |
---|
877 | waitack(); |
---|
878 | } |
---|
879 | } |
---|
880 | |
---|
881 | static void |
---|
882 | newdoc_gs() |
---|
883 | { |
---|
884 | static _Xconst char str[] = |
---|
885 | "Hxdvi$doc restore stop\n%%xdvimark\n"; |
---|
886 | |
---|
887 | if (debug & DBG_PS) Puts("Running newdoc_gs()"); |
---|
888 | |
---|
889 | if (GS_in_doc) { |
---|
890 | ++GS_pending; |
---|
891 | gs_io(str, sizeof(str) - 1); |
---|
892 | GS_mag = GS_shrink = -1; |
---|
893 | GS_in_doc = False; |
---|
894 | } |
---|
895 | } |
---|