summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/psgs.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/xdvik/psgs.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/xdvik/psgs.c')
-rw-r--r--Build/source/texk/xdvik/psgs.c1090
1 files changed, 1090 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/psgs.c b/Build/source/texk/xdvik/psgs.c
new file mode 100644
index 00000000000..e0d902848a4
--- /dev/null
+++ b/Build/source/texk/xdvik/psgs.c
@@ -0,0 +1,1090 @@
+/*========================================================================*\
+
+Copyright (c) 1994-2004 Paul Vojta
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+\*========================================================================*/
+
+#ifdef PS_GS /* entire file */
+
+
+#include <setjmp.h>
+#include <X11/Xatom.h>
+#include <sys/time.h> /* for timeval */
+
+#include <signal.h>
+
+/* Condition for retrying a write */
+#include <errno.h>
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+#include "psgs.h"
+
+#include "kpathsea/c-pathmx.h"
+#include "dvi-init.h"
+#include "dvi-draw.h"
+#include "events.h"
+#include "statusline.h"
+#include "special.h"
+#include "util.h"
+
+
+#ifdef X_NOT_STDC_ENV
+extern int errno;
+#endif
+
+#ifdef EWOULDBLOCK
+# ifdef EAGAIN
+# define AGAIN_CONDITION (errno == EWOULDBLOCK || errno == EAGAIN)
+# else
+# define AGAIN_CONDITION (errno == EWOULDBLOCK)
+# endif
+#else
+# ifdef EAGAIN
+# define AGAIN_CONDITION (errno == EAGAIN)
+# endif
+#endif
+
+#if HAVE_POLL
+# include <poll.h>
+# define XIO_IN POLLIN
+# define XIO_OUT POLLOUT
+#else
+# define XIO_IN 1
+# define XIO_OUT 2
+#endif
+
+#ifdef MOTIF
+# include <Xm/Xm.h>
+#endif
+
+extern const char psheader[];
+extern unsigned psheaderlen;
+
+Boolean gs_postpone_prescan = False;
+
+/* global procedures (besides initGS) */
+
+static void toggle_gs(int flag);
+static void destroy_gs(void);
+static void interrupt_gs(void);
+static void endpage_gs(void);
+static void drawbegin_gs(int, int, const char *);
+static void drawbegin_gs_box(int, int, const char *);
+void drawraw_gs(const char *);
+static void drawfile_gs(const char *, FILE *);
+static void drawend_gs(const char *);
+static void beginheader_gs(void);
+static void endheader_gs(void);
+static void newdoc_gs(void);
+
+static struct psprocs gs_procs = {
+ /* toggle */ toggle_gs,
+ /* destroy */ destroy_gs,
+ /* interrupt */ interrupt_gs,
+ /* endpage */ endpage_gs,
+ /* drawbegin */ drawbegin_gs,
+ /* drawraw */ drawraw_gs,
+ /* drawfile */ drawfile_gs,
+ /* drawend */ drawend_gs,
+ /* beginheader */ beginheader_gs,
+ /* endheader */ endheader_gs,
+ /* newdoc */ newdoc_gs
+};
+
+static int std_io[2];
+Pixmap bpixmap;
+
+#define GS_fd (std_io[0])
+
+ /* some arguments are filled in later */
+static char arg4[] = "-dDEVICEWIDTH=xxxxxxxxxx";
+static char arg5[] = "-dDEVICEHEIGHT=xxxxxxxxxx";
+
+static const char *argv[] = {
+ NULL, NULL, "-dNOPAUSE", "-q", arg4, arg5,
+ "-dDEVICEXRESOLUTION=72",
+ "-dDEVICEYRESOLUTION=72",
+ "-dNOSAFER", "-dNOEPS", NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static unsigned int GS_page_w; /* how big our current page is */
+static unsigned int GS_page_h;
+static Boolean GS_alpha; /* remember if we are using the alpha driver */
+static unsigned long GS_mag; /* magnification currently in use */
+static int GS_shrink; /* shrink factor currently in use */
+static Boolean GS_active; /* if we've started a page yet */
+static int GS_pending; /* number of ack's we're expecting */
+static char GS_outb[257]; /* circular output buffer */
+static char *GS_outb_in; /* next position in output buffer */
+static char *GS_outb_out; /* next byte to come out of buffer */
+#define GS_outb_limit (GS_outb + sizeof GS_outb) /* last+1 byte */
+static int GS_write_ack; /* flag to set when done writing */
+static Boolean GS_in_header; /* if we're sending a header */
+static Boolean GS_in_doc; /* if we've sent header information */
+static int GS_ev_mask; /* events for which we'll stop */
+static int GS_die_ack = 0; /* flags to set when GS dies */
+static Boolean GS_timer_set; /* if there's a timer set */
+static Boolean GS_old; /* if we're using gs 2.xx */
+
+#define GS_MASK_NORMAL EV_GE_NEWPAGE
+#define GS_MASK_HEADER EV_GE_PS_TOGGLE
+#define GS_MASK_INIT (EV_GE_TERM | EV_PS_TOGGLE)
+
+static Atom gs_atom;
+static Atom gs_colors_atom;
+
+#define Landscape 90
+
+#define LINELEN 81
+static char line[LINELEN + 1];
+static char *linepos = line;
+static char ackstr[] = "\347\310\376";
+static char oldstr[] = "\347\310\375";
+
+static void gs_died(int, struct xchild *);
+
+static char *read_from_gs(int);
+static void write_to_gs(int);
+
+static struct xio gs_xio = {NULL, 0, XIO_IN,
+#if HAVE_POLL
+ NULL,
+#endif
+ read_from_gs, write_to_gs
+};
+
+static struct xchild gs_child = { NULL, 0, True, NULL, NULL, NULL, gs_died };
+#define GS_pid (gs_child.pid)
+
+/* It seems that XtTimers don't work reliably with xdvi's event treatment;
+ so use custom timers for the time being. Search for TIMER_PROBLEM_FIXED
+ to locate the relevant code parts.
+*/
+
+#if TIMER_PROBLEM_FIXED
+static XtIntervalId gs_timeout_id = 0;
+static void gs_timer_proc(XtPointer client_data, XtIntervalId * id);
+static struct xtimer gs_timer = {NULL, {0, 0}, gs_timer_proc};
+#else
+static void gs_alarm(struct xtimer *timer);
+static struct xtimer gs_timer = {NULL, {0, 0}, XTM_DEFAULT, gs_alarm
+#if XDVI_XT_TIMER_HACK
+ , NULL, NULL
+#endif
+};
+#endif
+
+
+static void
+showto(char *q)
+{
+ char *p = line;
+ char *p1;
+
+ while (p < q) {
+ p1 = memchr(p, '\n', q - p);
+ if (p1 == NULL)
+ p1 = q;
+ *p1 = '\0';
+ printf("gs: %s\n", p);
+ p = p1 + 1;
+ }
+}
+
+static char *
+read_from_gs(int fd)
+{
+ int bytes;
+ char *line_end = NULL;
+ char *p;
+
+ for (;;) {
+
+ bytes = read(fd, linepos, line + LINELEN - linepos);
+ if (bytes < 0) {
+ if (AGAIN_CONDITION)
+ break;
+ perror("xdvik: read_from_gs");
+ break;
+ }
+ line_end = linepos + bytes;
+
+ if (bytes == 0) {
+ if (GS_pid != 0)
+ XDVI_WARNING((stderr, "Read_from_gs returned 0 bytes"));
+ break;
+ }
+
+ /* Check for ack strings */
+ for (p = line; p < line_end - 2; ++p) {
+ p = memchr(p, '\347', line_end - p - 2);
+ if (p == NULL) break;
+ if (memcmp(p, ackstr, 3) == 0) {
+ --GS_pending;
+ if (GS_pending == 0)
+ globals.ev.flags |= EV_ACK;
+ if (globals.debug & DBG_PS)
+ printf("Got GS ack; %d pending.\n", GS_pending);
+ } else if (memcmp(p, oldstr, 3) == 0) {
+ if (globals.debug & DBG_PS)
+ puts("Using old GS version.");
+ GS_old = True;
+ } else
+ continue;
+
+ showto(p);
+ p += 3;
+ memmove(line, p, line_end - p);
+ line_end -= p - line;
+ linepos = p = line;
+ --p;
+ }
+ *line_end = '\0';
+ p = rindex(linepos, '\n');
+ if (p != NULL) {
+ ++p;
+ showto(p);
+ memmove(line, p, line_end - p);
+ line_end -= p - line;
+ }
+ linepos = line_end;
+ /*
+ * Normally we'd hold text until a newline character, but the
+ * buffer is full. So we flush it, being careful not to cut up an
+ * ack string.
+ */
+ if (linepos >= line + LINELEN) {
+ p = line + LINELEN;
+ if ((*--p != '\347' && *--p != '\347' && *--p != '\347')
+ || (memcmp(p, ackstr, line + LINELEN - p) != 0
+ && memcmp(p, oldstr, line + LINELEN - p) != 0))
+ p = line + LINELEN;
+ *p = '\0';
+ printf("gs: %s\n", line);
+ *p = '\347';
+ linepos = line;
+ while (p < line + LINELEN)
+ *linepos++ = *p++;
+ }
+ }
+ return line_end; /* dummy */
+}
+
+static void
+write_to_gs(int fd)
+{
+ char *send_end;
+ int bytes;
+
+ UNUSED(fd);
+
+ for (;;) {
+ send_end = GS_outb_in;
+ if (send_end < GS_outb_out)
+ send_end = GS_outb_limit;
+ bytes = write(GS_fd, GS_outb_out, send_end - GS_outb_out);
+
+ if (bytes < 0) {
+ if (AGAIN_CONDITION)
+ break;
+ perror("xdvik: write_to_gs");
+ break;
+ }
+
+ GS_outb_out += bytes;
+ if (GS_outb_out == GS_outb_limit)
+ GS_outb_out = GS_outb;
+ if (GS_outb_out == GS_outb_in) { /* if buffer is empty */
+ gs_xio.xio_events = XIO_IN;
+#if HAVE_POLL
+ if (gs_xio.pfd != NULL) /* write_to_gs is called directly */
+ gs_xio.pfd->events = XIO_IN;
+#endif
+ break;
+ }
+ }
+ globals.ev.flags |= GS_write_ack;
+ GS_write_ack = 0;
+}
+
+/*
+ * Main routine for writing to the GS interpreter.
+ */
+
+static void
+gs_send(const char *cp, size_t len)
+{
+ const char *cp_end = cp + len;
+ char *send_end;
+ size_t bytes;
+ char *old_out;
+ Boolean interrupting;
+
+ if (GS_pid == 0 || (globals.ev.flags & GS_ev_mask))
+ return;
+
+ /*
+ * Because cp might reside on the stack, don't return until we've
+ * copied all of it to our circular output buffer.
+ * Note that GS_outb_out == GS_outb_in means that the buffer is empty.
+ */
+
+ GS_timer_set = interrupting = False;
+ for (;;) {
+ send_end = GS_outb_out;
+ if (send_end == GS_outb)
+ send_end = GS_outb_limit;
+ --send_end;
+ if (send_end < GS_outb_in)
+ send_end = GS_outb_limit;
+ bytes = send_end - GS_outb_in;
+ if (bytes > 0) {
+ if (bytes >= (unsigned)(cp_end - cp))
+ bytes = cp_end - cp;
+ memcpy(GS_outb_in, cp, bytes);
+ cp += bytes;
+ GS_outb_in += bytes;
+ if (GS_outb_in == GS_outb_limit)
+ GS_outb_in = GS_outb;
+ if (cp < cp_end)
+ continue;
+ }
+
+ /* The buffer is now full --or-- we've run out of data */
+ old_out = GS_outb_out;
+ if (!(gs_xio.xio_events & XIO_OUT)) { /* restart output */
+ gs_xio.xio_events = XIO_IN | XIO_OUT;
+#if HAVE_POLL
+ if (gs_xio.pfd != NULL)
+ gs_xio.pfd->events = POLLIN | POLLOUT;
+#endif
+ write_to_gs(GS_fd);
+ if (GS_outb_out != old_out) {
+ if (cp == cp_end)
+ break;
+ else
+ continue;
+ }
+ }
+
+ if (cp == cp_end)
+ break;
+
+ GS_die_ack = GS_write_ack = EV_ACK;
+ for (;;) { /* loop because there may be stray ACKs */
+ if (!interrupting) {
+ (void) read_events(GS_ev_mask | EV_ACK);
+ globals.ev.flags &= ~EV_ACK;
+
+ if (GS_pid == 0) { /* if GS died */
+ GS_die_ack = 0;
+ return;
+ }
+
+ if (GS_outb_out != old_out) /* if more room in buffer */
+ break;
+
+ if (globals.ev.flags & GS_ev_mask) { /* if a serious event */
+ if (globals.debug & DBG_PS)
+ puts("Setting timeout in gs_send()");
+
+ set_timer(&gs_timer, resource.gs_timeout);
+ GS_timer_set = interrupting = True;
+ }
+ }
+ else {
+ (void) read_events(EV_GE_TERM | EV_PS_TOGGLE | EV_ACK);
+ globals.ev.flags &= ~EV_ACK;
+
+ if (GS_outb_out != old_out) /* if more room in buffer */
+ break;
+
+ if (GS_timer_set) /* if timer still set */
+ cancel_timer(&gs_timer);
+
+ destroy_gs();
+ GS_die_ack = 0;
+ return;
+ }
+ }
+ }
+
+ if (GS_timer_set) /* if timer still set */
+ cancel_timer(&gs_timer);
+
+ GS_die_ack = GS_write_ack = 0;
+}
+
+static void
+#if TIMER_PROBLEM_FIXED
+gs_timer_proc(XtPointer client_data, XtIntervalId * id)
+#else
+gs_alarm(struct xtimer *timer)
+#endif
+{
+ UNUSED(timer);
+
+ if (globals.debug & DBG_PS)
+ puts("GS timeout expired");
+
+ globals.ev.flags |= EV_ACK;
+ GS_timer_set = False;
+#if TIMER_PROBLEM_FIXED
+ gs_timeout_id = 0;
+#endif
+}
+
+/*
+ * Wait for acknowledgement from GS.
+ */
+
+static void
+waitack(void)
+{
+ if (GS_pending == 0) {
+ globals.ev.flags &= ~EV_ACK;
+ return;
+ }
+
+ GS_die_ack = EV_ACK;
+
+ for (;;) { /* loop because there might be stray ACKs. */
+/* fprintf(stderr, "looping for stray ACKs, page %d\n", current_page); */
+ (void) read_events(EV_GE_ACK);
+ globals.ev.flags &= ~EV_ACK;
+
+ if (GS_pending == 0) {
+ GS_die_ack = 0;
+ return;
+ }
+ if (globals.ev.flags & EV_GE_ACK)
+ break;
+ }
+
+ if (globals.debug & DBG_PS)
+ puts("Setting timeout in waitack()");
+
+#if TIMER_PROBLEM_FIXED
+ if (gs_timeout_id)
+ XtRemoveTimeOut(gs_timeout_id);
+ gs_timeout_id = XtAppAddTimeOut(app, resource.gs_timeout,
+ gs_timer_proc, (XtPointer) NULL);
+#else
+ set_timer(&gs_timer, resource.gs_timeout);
+#endif
+ GS_timer_set = True;
+
+ (void) read_events(EV_GE_TERM | EV_PS_TOGGLE | EV_ACK);
+ globals.ev.flags &= ~EV_ACK;
+
+ if (GS_timer_set) {
+#if TIMER_PROBLEM_FIXED
+ XtRemoveTimeOut(gs_timeout_id);
+ gs_timeout_id = 0;
+#else
+ cancel_timer(&gs_timer);
+#endif
+ }
+ GS_die_ack = 0;
+
+ if (GS_pending > 0)
+ destroy_gs();
+}
+
+
+/*
+ * Fork a process to run ghostscript. This is done using the
+ * x11 device (which needs to be compiled in). Normally the x11
+ * device uses ClientMessage events to communicate with the calling
+ * program, but we don't do this. The reason for using the ClientMessage
+ * events is that otherwise ghostview doesn't know when a non-conforming
+ * postscript program calls showpage. That doesn't affect us here,
+ * since in fact we disable showpage.
+ *
+ * SAFER mode is handled by providing both the -dNOSAFER and -dSAFER
+ * options. Ghostscript versions 7.04 and earlier ignore -dNOSAFER and use
+ * -dSAFER; the code in strsafe is ignored since .locksafe is not present.
+ * In versions 7.04 and higher, -dNOSAFER overrides -dSAFER; SAFER mode is
+ * optionally turned on by sending the strsafe string. It is possible in some
+ * versions of gs prior to 7.04 to use -dDELAYSAFER instead of -dNOSAFER, but
+ * there's no point in doing that since .locksafe is not defined in those
+ * versions. I don't know where 7.03 fits in on all of this (but it works with
+ * that version as well).
+ */
+
+Boolean
+initGS(void)
+{
+ char buf[100];
+ static Boolean did_putenv = False;
+ /*
+ * This string reads chunks (delimited by %%xdvimark).
+ * The first character of a chunk tells whether a given chunk
+ * is to be done within save/restore or not.
+ * The `H' at the end tells it that the first group is a
+ * header; i.e., no save/restore.
+ * `execute' is unique to ghostscript.
+ */
+
+ static const char strsafe[] =
+ "{ << /PermitFileReading [ (*) ] /PermitFileWriting [ ] /PermitFileControl [ ] "
+ ">> setuserparams .locksafe "
+ "} stopped pop\n";
+ static const char str1[] =
+ "/xdvi$run {$error /newerror false put {currentfile cvx execute} stopped pop} "
+ "def "
+ "/xdvi$ack (\347\310\376) def "
+ "/xdvi$dslen countdictstack def "
+ "{currentfile read pop 72 eq "
+ "{xdvi$run} "
+ "{/xdvi$sav save def xdvi$run "
+ "clear countdictstack xdvi$dslen sub {end} repeat xdvi$sav restore} "
+ "ifelse "
+ "{(%%xdvimark) currentfile =string {readline} stopped "
+ "{clear} {pop eq {exit} if} ifelse }loop "
+ "flushpage xdvi$ack print flush "
+ "}loop\nH";
+
+ static const char str2[] =
+ "[0 1 1 0 0 0] concat\n"
+ "revision 300 lt{(\347\310\375) print flush}if\n"
+ "stop\n%%xdvimark\n";
+
+ /*
+ * If we're prescanning *before* setting up the widgets (to get the
+ * page size, for example), then postpone starting up ghostscript.
+ */
+ if (mane.win == (Window)0) {
+ if (globals.debug & DBG_PS)
+ puts("Hit PS header in early prescan; postponing.");
+ psp = no_ps_procs;
+ gs_postpone_prescan = True;
+ return True;
+ }
+
+ if (globals.debug & DBG_PS)
+ puts("Running initGS ...");
+
+ gs_atom = XInternAtom(DISP, "GHOSTVIEW", False);
+ /* send bpixmap, orientation, bbox (in pixels), and h & v resolution */
+ sprintf(buf, "%ld %d 0 0 %u %u 72 72",
+ bpixmap, /* bpixmap */
+ Landscape, /* orientation */
+ GS_page_h = globals.page.h, GS_page_w = globals.page.w);
+
+ XChangeProperty(DISP, mane.win, gs_atom, XA_STRING, 8,
+ PropModeReplace, (unsigned char *)buf, strlen(buf));
+ GS_alpha = resource.gs_alpha;
+
+ gs_colors_atom = XInternAtom(DISP, "GHOSTVIEW_COLORS", False);
+ sprintf(buf, "%s %ld %ld", resource.gs_palette,
+ color_data[0].pixel, color_data[1].pixel);
+
+ XChangeProperty(DISP, mane.win, gs_colors_atom, XA_STRING, 8,
+ PropModeReplace, (unsigned char *)buf, strlen(buf));
+
+ if (!did_putenv) {
+ sprintf(buf, "%ld", mane.win);
+ xputenv("GHOSTVIEW", buf);
+ did_putenv = True;
+ }
+
+ XSync(DISP, False); /* update the window */
+
+ if (xpipe(std_io) != 0) {
+ perror("[xdvik] pipe");
+ return False;
+ }
+ fflush(stderr); /* to avoid double flushing */
+ GS_pid = vfork();
+ if (GS_pid == 0) { /* child */
+ const char **argvp = argv + 10;
+
+ argv[1] = resource.gs_alpha ? "-sDEVICE=x11alpha" : "-sDEVICE=x11";
+ sprintf(arg4 + 14, "%u", GS_page_w);
+ sprintf(arg5 + 15, "%u", GS_page_h);
+ if (resource.gs_safer)
+ *argvp++ = "-dSAFER";
+
+#if 0
+ if (resource.gs_alpha) {
+ *argvp++ = "-dGraphicsAlphaBits=4";
+ *argvp++ = "-dTextAlphaBits=4";
+ *argvp++ = "-dMaxBitmap=0";
+ }
+#endif
+
+ *argvp = "-";
+ (void) close(std_io[0]);
+ (void) dup2(std_io[1], 0);
+ (void) dup2(std_io[1], 1);
+ (void) dup2(std_io[1], 2);
+ (void) close(std_io[1]);
+
+ (void)execvp(argv[0] = resource.gs_path, (char *const *)argv);
+ /*TODO: use fork_process here?? */
+ XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
+ _exit(EXIT_FAILURE);
+ }
+
+ (void)close(std_io[1]);
+
+ if (GS_pid == -1) { /* error */
+ GS_pid = 0;
+ perror("[xdvik] vfork");
+ (void)close(GS_fd);
+
+ return False;
+ }
+
+ prep_fd(GS_fd, True); /* Set file descriptor for non-blocking I/O */
+
+ gs_child.name = xstrdup("gs");
+ gs_child.io = &gs_xio;
+ set_chld(&gs_child);
+
+ psp = gs_procs;
+ GS_active = False;
+ GS_in_header = True;
+ GS_pending = 1;
+ GS_mag = GS_shrink = -1;
+ gs_xio.fd = GS_fd;
+ gs_xio.xio_events = XIO_IN;
+ GS_write_ack = 0;
+ GS_outb_in = GS_outb_out = GS_outb;
+ set_io(&gs_xio);
+ GS_ev_mask = GS_MASK_INIT;
+ (void) signal(SIGPIPE, SIG_IGN);
+
+ if (resource.gs_safer)
+ gs_send(strsafe, (sizeof strsafe) - 1);
+ gs_send(str1, (sizeof str1) - 1);
+ gs_send(psheader, psheaderlen);
+ gs_send(str2, (sizeof str2) - 1);
+ waitack();
+ GS_in_header = False;
+ GS_ev_mask = GS_MASK_NORMAL;
+
+ if (GS_pid == 0) { /* if something happened */
+ destroy_gs();
+ return False;
+ }
+ if (resource.postscript == 0)
+ toggle_gs(0); /* if we got a 'v' already */
+ else {
+ scanned_page = scanned_page_ps = scanned_page_reset;
+ globals.ev.flags |= EV_NEWPAGE; /* ||| redraw the page */
+ longjmp(globals.ev.canit, 1);
+ }
+ return True;
+}
+
+static void
+toggle_gs(int flag) /* this routine is callable from within read_events(). */
+{
+ if (globals.debug & DBG_PS)
+ fprintf(stdout, "Toggling GS to %d", flag);
+
+ switch (flag) {
+ case 0:
+ psp.drawbegin = drawbegin_none;
+ break;
+ case 1:
+ psp.drawbegin = drawbegin_gs;
+ break;
+ default:
+ psp.drawbegin = drawbegin_gs_box;
+ break;
+ }
+}
+
+void
+gs_resume_prescan()
+{
+ if (globals.debug & DBG_PS)
+ puts("Resuming prescan");
+
+ gs_postpone_prescan = False;
+ if (!initGS()) /* this may not return */
+ psp = no_ps_procs;
+}
+
+static void
+gs_died(int status, struct xchild *child)
+{
+ UNUSED(status);
+ if (globals.debug & DBG_PS)
+ fprintf(stderr, "process %s died\n", child->name);
+ GS_pid = 0;
+ (child->io->read_proc)(child->io->fd);
+ if (linepos > line) {
+ *linepos = '\0';
+ printf("%s: %s\n", child->name, line);
+ linepos = line;
+ }
+ clear_io(child->io);
+ (void)close(child->io->fd);
+
+ scanned_page = scanned_page_ps = scanned_page_reset;
+ GS_active = GS_in_doc = False;
+ GS_pending = 0;
+ globals.ev.flags |= GS_die_ack;
+}
+
+static void
+destroy_gs(void)
+{
+ if (globals.debug & DBG_PS)
+ puts("Destroying GS process");
+ if (GS_pid != 0) {
+ if (kill(GS_pid, SIGKILL) < 0 && errno != ESRCH)
+ perror("xdvik: destroy_gs");
+ GS_pid = 0;
+ clear_chld(&gs_child);
+ read_from_gs(GS_fd);
+ if (linepos > line) {
+ *linepos = '\0';
+ printf("gs: %s\n", line);
+ linepos = line;
+ }
+ clear_io(&gs_xio);
+ (void) close(GS_fd);
+
+ scanned_page = scanned_page_ps = scanned_page_reset;
+ }
+ GS_active = GS_in_doc = False;
+ GS_pending = 0;
+}
+
+static void
+deactivate(void)
+{
+ static const char str[] = " stop\n%%xdvimark\n";
+ int saved_mask;
+
+ saved_mask = GS_ev_mask;
+ GS_ev_mask = 0;
+ gs_send(str, (sizeof str) - 1);
+ GS_ev_mask = saved_mask;
+
+ GS_active = False;
+}
+
+static void
+interrupt_gs(void)
+{
+ if (globals.debug & DBG_PS)
+ puts("Running interrupt_gs()");
+ /*
+ FIXME: added this special case, since otherwise EV_ACK might not get
+ reset at all, and hang xdvi forever when scrolling quickly through
+ a file that should be reloaded.
+ */
+ if (GS_pending == 0) {
+/* fprintf(stderr, "+++++++++ 1st case; waitack: %d\n", globals.ev.flags & EV_ACK); */
+ globals.ev.flags &= ~EV_ACK;
+ return;
+ }
+ else if (GS_pending < 0) {
+/* fprintf(stderr, "GS_pending: %d; waitack: %d\n", GS_pending, globals.ev.flags & EV_ACK); */
+ return; /* nothing to do */
+ }
+
+ /*
+ * ||| what I'd really like to do here is cause gs to execute
+ * the interrupt routine in errordict. But so far (gs 2.6.1)
+ * that has not been implemented in ghostscript.
+ */
+
+ if (GS_active)
+ deactivate();
+ waitack();
+}
+
+static void
+endpage_gs(void)
+{
+ if (globals.debug & DBG_PS)
+ puts("Running endpage_gs()");
+ if (GS_active) {
+ deactivate();
+ waitack();
+ }
+}
+
+/*
+ * Checks that the GS interpreter is running correctly.
+ */
+
+static void
+checkgs(Boolean in_header)
+{
+ char buf[150];
+
+ /* For gs 2, we pretty much have to start over to enlarge the window. */
+ if ((GS_old && (globals.page.w > GS_page_w || globals.page.h > GS_page_h))
+ || GS_alpha != resource.gs_alpha)
+ destroy_gs();
+
+ if (GS_pid == 0)
+ (void)initGS();
+
+ if (!GS_active) {
+ /* check whether page_w or page_h have increased */
+ if (globals.page.w > GS_page_w || globals.page.h > GS_page_h) {
+ if (globals.ev.flags & GS_ev_mask)
+ longjmp(globals.ev.canit, 1);
+ ++GS_pending;
+ sprintf(buf,
+ "H mark /HWSize [%d %d] /ImagingBBox [0 0 %d %d] "
+ "currentdevice putdeviceprops pop\n"
+ "initgraphics [0 1 1 0 0 0] concat stop\n"
+ "%%%%xdvimark\n",
+ GS_page_w = globals.page.w,
+ GS_page_h = globals.page.h,
+ globals.page.h,
+ globals.page.w);
+ gs_send(buf, strlen(buf));
+ if (!in_header) {
+ globals.ev.flags |= EV_NEWPAGE; /* ||| redraw the page */
+ longjmp(globals.ev.canit, 1);
+ }
+ }
+
+ if (magnification != GS_mag) {
+ if (globals.ev.flags & GS_ev_mask)
+ longjmp(globals.ev.canit, 1);
+ ++GS_pending;
+ sprintf(buf,
+ "H TeXDict begin /DVImag %ld 1000 div def end stop\n"
+ "%%%%xdvimark\n",
+ GS_mag = magnification);
+ gs_send(buf, strlen(buf));
+ }
+
+ if (mane.shrinkfactor != GS_shrink) {
+ if (globals.ev.flags & GS_ev_mask)
+ longjmp(globals.ev.canit, 1);
+ ++GS_pending;
+ sprintf(buf,
+ "H TeXDict begin %d %d div dup /Resolution X /VResolution X end stop\n"
+ "%%%%xdvimark\n",
+ resource.pixels_per_inch,
+ GS_shrink = mane.shrinkfactor);
+ gs_send(buf, strlen(buf));
+ }
+ }
+}
+
+static void
+drawbegin_gs(int xul, int yul, const char *cp)
+{
+ char buf[32];
+ static const char str[] = " TeXDict begin\n";
+
+ checkgs(False);
+
+ if (!GS_active) {
+ if (globals.ev.flags & GS_ev_mask)
+ longjmp(globals.ev.canit, 1);
+ ++GS_pending;
+ gs_send(str, (sizeof str) - 1);
+ GS_active = True;
+ }
+
+ /* This allows the X side to clear the page */
+ XSync(DISP, False);
+
+ sprintf(buf, "%d %d moveto\n", xul, yul);
+ gs_send(buf, strlen(buf));
+/* gs_send(clear_bg, strlen(clear_bg)); */
+ if (globals.debug & DBG_PS)
+ printf("drawbegin at %d,%d: sending `%s'\n", xul, yul, cp);
+
+ /*
+ * added SU 2000/12/18:
+ * Try to detect some simple PS rotating commands, and warn about them.
+ * There are surely more than that ...
+ */
+ if (strstr(cp, "rotate") != NULL /* used by graphics.sty */
+ || strstr(cp, "RotBegin") != NULL /* used by pstricks */
+ ) {
+ statusline_print(STATUS_SHORT,
+ "Warning: PS code on page %d may contain rotation, "
+ "which is not supported by xdvi", current_page + 1);
+ }
+
+ gs_send(cp, strlen(cp));
+}
+
+static void
+drawbegin_gs_box(int xul, int yul, const char *cp)
+{
+ drawbegin_gs(xul, yul, cp);
+}
+
+
+void
+drawraw_gs(const char *cp)
+{
+ if (!GS_active)
+ return;
+ if (globals.debug & DBG_PS)
+ printf("raw ps sent to context: %s\n", cp);
+ gs_send(cp, strlen(cp));
+ gs_send("\n", 1);
+}
+
+static void
+drawfile_gs(const char *cp, FILE *f)
+{
+ char canonical_path[MAXPATHLEN + 1];
+ char *ret;
+
+ fclose(f); /* don't need it */
+
+ if (!GS_active)
+ return;
+
+ if (globals.debug & DBG_PS)
+ printf("original path: |%s|\n", cp);
+/* drawraw_gs("newpath 0 0 moveto 0 1000 rlineto 1000 0 rlineto 0 -1000 rlineto closepath 1 setgray fill"); */
+#if 0
+ /* if expand_symlinks or remove_dots from kpathsea were public,
+ we could use the following: */
+ ret = remove_dots(expand_symlinks(cp));
+#endif
+
+ ret = REALPATH(cp, canonical_path);
+ if (ret == NULL)
+ XDVI_WARNING((stderr, "Couldn't canonicalize path \"%s\": %s. Sending to gs unchanged.",
+ cp, strerror(errno)));
+ else
+ cp = canonical_path;
+
+ if (globals.debug & DBG_PS)
+ printf("expanded path: |%s|\n", cp);
+
+ gs_send("(", 1);
+ gs_send(cp, strlen(cp));
+ gs_send(")run\n", 5);
+/* { */
+/* char *clear_bg = "newpath 0 0 moveto 0 1000 rlineto 1000 0 rlineto 0 -1000 rlineto closepath .4 setgray fill\n"; */
+/* gs_send(clear_bg, strlen(clear_bg)); */
+/* } */
+}
+
+static void
+drawend_gs(const char *cp)
+{
+ if (!GS_active)
+ return;
+ if (globals.debug & DBG_PS)
+ printf("end ps: %s\n", cp);
+ gs_send(cp, strlen(cp));
+ gs_send("\n", 1);
+ save_bbox();
+}
+
+static void
+beginheader_gs(void)
+{
+ static const char str[] = "Hsave /xdvi$doc exch def\n";
+
+ if (globals.debug & DBG_PS)
+ puts("Running beginheader_gs()");
+
+ checkgs(True);
+
+ if (GS_active) {
+ if (!GS_in_header)
+ XDVI_FATAL((stderr, "Internal error in beginheader_gs()."));
+ return;
+ }
+
+ if (globals.ev.flags & GS_ev_mask) {
+/* fprintf(stderr, "longjmp!"); */
+ longjmp(globals.ev.canit, 1);
+ }
+
+ GS_in_header = True;
+ GS_ev_mask = GS_MASK_HEADER;
+ ++GS_pending;
+ if (GS_in_doc)
+ gs_send("H", 1);
+ else {
+ gs_send(str, (sizeof str) - 1);
+ GS_in_doc = True;
+ }
+ GS_active = True;
+}
+
+static void
+endheader_gs(void)
+{
+ if (globals.debug & DBG_PS)
+ puts("Running endheader_gs()");
+
+ if (GS_active) {
+ deactivate();
+ waitack();
+ GS_in_header = False;
+ GS_ev_mask = GS_MASK_NORMAL;
+ }
+}
+
+static void
+newdoc_gs(void)
+{
+ static const char str[] = "Hxdvi$doc restore stop\n%%xdvimark\n";
+
+ if (globals.debug & DBG_PS)
+ puts("Running newdoc_gs()");
+
+ if (GS_in_doc) {
+ ++GS_pending;
+ gs_send(str, (sizeof str) - 1);
+ GS_mag = GS_shrink = -1;
+ GS_in_doc = False;
+
+ if (!GS_old) GS_page_w = GS_page_h = 0;
+ }
+}
+
+void erasepage_gs(void)
+{
+ /* In buffered mode, gs has no way of knowing that the screen has been
+ erased. Clear the gs window buffer with an erasepage command. */
+
+ if (resource.gs_alpha && GS_pid > 0 && !GS_active) {
+ if (globals.debug & DBG_PS)
+ puts("erasing page!");
+ beginheader_gs();
+ drawraw_gs("erasepage");
+ endheader_gs();
+ }
+}
+
+#else
+/* silence `empty compilation unit' warnings */
+static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
+#endif /* PS_GS */