1997-10-18 11:34:02 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1994 Paul Vojta. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ||| to do:
|
|
|
|
* *interpreter resource
|
|
|
|
* -safer argument
|
|
|
|
* palette resource and arguments
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "oconfig.h"
|
|
|
|
#ifdef PS_GS /* whole file */
|
|
|
|
|
|
|
|
#include "kpathsea/c-pathmx.h"
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <sys/time.h> /* for timeval */
|
|
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#ifdef NeXT
|
|
|
|
typedef int pid_t;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* if POSIX O_NONBLOCK is not available, use O_NDELAY */
|
|
|
|
#if !defined(O_NONBLOCK) && defined(O_NDELAY)
|
|
|
|
#define O_NONBLOCK O_NDELAY
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Condition for retrying a write */
|
|
|
|
#include <errno.h>
|
|
|
|
#ifdef EWOULDBLOCK
|
|
|
|
#ifdef EAGAIN
|
|
|
|
#define AGAIN_CONDITION (errno == EWOULDBLOCK || errno == EAGAIN)
|
|
|
|
#else /* EAGAIN */
|
|
|
|
#define AGAIN_CONDITION (errno == EWOULDBLOCK)
|
|
|
|
#endif /* EAGAIN */
|
|
|
|
#else /* EWOULDBLOCK */
|
|
|
|
#ifdef EAGAIN
|
|
|
|
#define AGAIN_CONDITION (errno == EAGAIN)
|
|
|
|
#endif /* EAGAIN */
|
|
|
|
#endif /* EWOULDBLOCK */
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef STREAMSCONN
|
|
|
|
#include <poll.h>
|
|
|
|
#else
|
|
|
|
#ifdef _AIX
|
|
|
|
#include <sys/select.h> /* for fd_set stuff on RS/6000 */
|
|
|
|
#else
|
|
|
|
#ifdef HAVE_SYS_BSDTYPES_H
|
|
|
|
#include <sys/bsdtypes.h> /* for fd_set on ISC 4.0 */
|
|
|
|
#endif /* not <sys/bsdtypes.h> */
|
|
|
|
#endif /* not _AIX */
|
|
|
|
#endif /* not STREAMSCONN */
|
|
|
|
|
|
|
|
#if HAS_SIGIO
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#ifndef FASYNC
|
|
|
|
#undef HAS_SIGIO
|
|
|
|
#define HAS_SIGIO 0
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef VFORK
|
|
|
|
#if VFORK == include
|
|
|
|
#include <vfork.h>
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define vfork fork
|
|
|
|
#endif
|
|
|
|
|
|
|
|
extern _Xconst char psheader[];
|
|
|
|
extern int psheaderlen;
|
|
|
|
|
|
|
|
#define postscript _postscript
|
|
|
|
#define fore_Pixel _fore_Pixel
|
|
|
|
#define back_Pixel _back_Pixel
|
|
|
|
|
1997-11-26 13:48:15 +00:00
|
|
|
extern void qt_processEvents();
|
|
|
|
|
1997-10-18 11:34:02 +00:00
|
|
|
/* global procedures (besides initGS) */
|
|
|
|
|
|
|
|
static void toggle_gs ARGS((void));
|
|
|
|
static void destroy_gs ARGS((void));
|
|
|
|
static void interrupt_gs ARGS((void));
|
|
|
|
static void endpage_gs ARGS((void));
|
|
|
|
static void drawbegin_gs ARGS((int, int, char *));
|
|
|
|
static void drawraw_gs ARGS((char *));
|
|
|
|
static void drawfile_gs ARGS((char *));
|
|
|
|
static void drawend_gs ARGS((char *));
|
|
|
|
|
|
|
|
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};
|
|
|
|
|
|
|
|
static int std_in[2];
|
|
|
|
static int std_out[2];
|
|
|
|
|
|
|
|
#define GS_in (std_in[1])
|
|
|
|
#define GS_out (std_out[0])
|
|
|
|
|
|
|
|
static char *argv[] = {"gs", "-sDEVICE=x11alpha", "-dNOPAUSE", "-dSAFER", "-q", "-", NULL};
|
|
|
|
|
|
|
|
static pid_t GS_pid;
|
|
|
|
static unsigned int GS_page_w; /* how big our current page is */
|
|
|
|
static unsigned int GS_page_h;
|
|
|
|
static int 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 Boolean GS_sending; /* if we're in the middle of send() */
|
|
|
|
static Boolean GS_pending_int; /* if interrupt rec'd while in send() */
|
|
|
|
|
|
|
|
static Atom gs_atom;
|
|
|
|
static Atom gs_colors_atom;
|
|
|
|
#define XtPageOrientationPortrait 0
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Our replacement for setenv(), which is not available on all systems.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef HAVE_SETENV /* define this if you're a performance freak and
|
|
|
|
if your system has setenv. */
|
|
|
|
#define setenv(var, str, repl) _setenv(var, str) /* repl always True */
|
|
|
|
|
|
|
|
extern char **environ;
|
|
|
|
|
|
|
|
static void
|
|
|
|
_setenv(var, str)
|
|
|
|
_Xconst char *var;
|
|
|
|
_Xconst char *str;
|
|
|
|
{
|
|
|
|
int len1;
|
|
|
|
int len2;
|
|
|
|
char *newvar;
|
|
|
|
char **linep;
|
|
|
|
static Boolean malloced = False;
|
|
|
|
|
|
|
|
len1 = strlen(var);
|
|
|
|
len2 = strlen(str) + 1;
|
|
|
|
newvar = xmalloc((unsigned int) len1 + len2 + 1, "_setenv");
|
|
|
|
(void) bcopy(var, newvar, len1);
|
|
|
|
newvar[len1++] = '=';
|
|
|
|
(void) bcopy(str, newvar + len1, len2);
|
|
|
|
for (linep = environ; *linep != NULL; ++linep)
|
|
|
|
if (memcmp(*linep, newvar, len1) == 0) {
|
|
|
|
*linep = newvar;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
len1 = linep - environ;
|
|
|
|
if (malloced) {
|
|
|
|
environ = (char **) realloc((char *) environ,
|
|
|
|
(unsigned int) (len1 + 2) * sizeof(char *));
|
|
|
|
if (environ == NULL)
|
|
|
|
oops("! Cannot allocate %d bytes for string list in _setenv.\n",
|
|
|
|
(len1 + 2) * sizeof(char *));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
linep = (char **) xmalloc((unsigned int)(len1 + 2) * sizeof(char *),
|
|
|
|
"string list in _setenv");
|
|
|
|
(void) bcopy((char *) environ, (char *) linep,
|
|
|
|
len1 * sizeof(char *));
|
|
|
|
environ = linep;
|
|
|
|
malloced = True;
|
|
|
|
}
|
|
|
|
environ[len1] = newvar;
|
|
|
|
environ[len1 + 1] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_SETENV */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ghostscript I/O code. This should send PS code to ghostscript,
|
|
|
|
* receive acknowledgements, and receive X events in the meantime.
|
|
|
|
* It also checks for SIGPIPE errors.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
static int numfds;
|
|
|
|
static fd_set readfds;
|
|
|
|
static fd_set writefds;
|
|
|
|
#define XDVI_ISSET(a, b, c) FD_ISSET(a, b)
|
|
|
|
#else /* STREAMSCONN */
|
|
|
|
struct pollfd fds[3] = {{0, POLLOUT, 0},
|
|
|
|
{0, POLLIN, 0},
|
|
|
|
{0, POLLIN, 0}};
|
|
|
|
#define XDVI_ISSET(a, b, c) (fds[c].revents)
|
|
|
|
#endif /* STREAMSCONN */
|
|
|
|
|
|
|
|
#define LINELEN 81
|
|
|
|
static char line[LINELEN + 1];
|
|
|
|
static char *linepos = line;
|
|
|
|
static char ackstr[] = "\347\310\376";
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_from_gs() {
|
|
|
|
int bytes;
|
|
|
|
char *line_end;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
bytes = read(GS_out, linepos, line + LINELEN - linepos);
|
|
|
|
if (bytes < 0) return;
|
|
|
|
line_end = linepos + bytes;
|
|
|
|
/* 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 (debug & DBG_PS)
|
|
|
|
Printf("Got GS ack; %d pending.\n", GS_pending);
|
|
|
|
if (p > line) {
|
|
|
|
*p = '\0';
|
|
|
|
Printf("gs: %s\n", line);
|
|
|
|
}
|
|
|
|
p += 3;
|
|
|
|
(void) bcopy(p, line, line_end - p);
|
|
|
|
line_end -= p - line;
|
|
|
|
linepos = p = line;
|
|
|
|
--p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
p = memchr(linepos, '\n', line_end - linepos);
|
|
|
|
if (p == NULL) break;
|
|
|
|
*p = '\0';
|
|
|
|
Printf("gs: %s\n", line);
|
|
|
|
++p;
|
|
|
|
(void) bcopy(p, line, line_end - p);
|
|
|
|
line_end -= p - line;
|
|
|
|
linepos = 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)
|
|
|
|
p = line + LINELEN;
|
|
|
|
*p = '\0';
|
|
|
|
Printf("gs: %s\n", line);
|
|
|
|
*p = '\347';
|
|
|
|
linepos = line;
|
|
|
|
while (p < line + LINELEN) *linepos++ = *p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For handling of SIGPIPE signals from send()
|
|
|
|
*/
|
|
|
|
|
|
|
|
static Boolean sigpipe_error = False;
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
static void
|
|
|
|
gs_sigpipe_handler(sig, code, scp, addr)
|
|
|
|
int sig;
|
|
|
|
int code;
|
|
|
|
struct sigcontext *scp;
|
|
|
|
char *addr;
|
|
|
|
{
|
|
|
|
sigpipe_error = True;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _POSIX_SOURCE
|
|
|
|
static struct sigaction sigpipe_handler_struct;
|
|
|
|
/* initialized to {gs_sigpipe_handler, (sigset_t) 0, 0} in initGS */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This actually sends the bytes to ghostscript.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
send(cp, len)
|
|
|
|
_Xconst char *cp;
|
|
|
|
int len;
|
|
|
|
{
|
|
|
|
int bytes;
|
|
|
|
#ifdef _POSIX_SOURCE
|
|
|
|
struct sigaction orig;
|
|
|
|
#else
|
|
|
|
void (*orig)();
|
|
|
|
#endif
|
|
|
|
#ifdef STREAMSCONN
|
|
|
|
int retval;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (GS_pid < 0) return;
|
|
|
|
#ifdef _POSIX_SOURCE
|
|
|
|
(void) sigaction(SIGPIPE, &sigpipe_handler_struct, &orig);
|
|
|
|
#else
|
|
|
|
orig = signal(SIGPIPE, gs_sigpipe_handler);
|
|
|
|
#endif
|
|
|
|
sigpipe_error = False;
|
|
|
|
GS_sending = True;
|
|
|
|
|
|
|
|
#if HAS_SIGIO
|
|
|
|
(void) fcntl(ConnectionNumber(DISP), F_SETFL,
|
|
|
|
fcntl(ConnectionNumber(DISP), F_GETFL, 0) & ~FASYNC);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
FD_ZERO(&writefds);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
FD_SET(ConnectionNumber(DISP), &readfds);
|
|
|
|
FD_SET(GS_in, &writefds);
|
|
|
|
FD_SET(GS_out, &readfds);
|
|
|
|
|
|
|
|
if (select(numfds, &readfds, &writefds, (fd_set *) NULL,
|
|
|
|
(struct timeval *) NULL) < 0 && errno != EINTR) {
|
|
|
|
perror("select (gs_send)");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#else /* STREAMSCONN */
|
|
|
|
for (;;) {
|
|
|
|
retval = poll(fds, XtNumber(fds), -1);
|
|
|
|
if (retval >= 0 || errno != EAGAIN) break;
|
|
|
|
}
|
|
|
|
if (retval < 0) {
|
|
|
|
perror("poll (gs_send)");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* STREAMSCONN */
|
|
|
|
|
|
|
|
if (XDVI_ISSET(GS_out, &readfds, 1))
|
|
|
|
read_from_gs();
|
|
|
|
if (XDVI_ISSET(GS_in, &writefds, 0)) {
|
|
|
|
bytes = write(GS_in, cp, len);
|
|
|
|
if (bytes == -1) {
|
|
|
|
if (!AGAIN_CONDITION) perror("gs_send");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cp += bytes;
|
|
|
|
len -= bytes;
|
|
|
|
if (len == 0) break;
|
|
|
|
}
|
|
|
|
if (sigpipe_error) break;
|
|
|
|
}
|
|
|
|
if (XDVI_ISSET(ConnectionNumber(DISP), &readfds, 2)) {
|
|
|
|
#ifndef KDVI
|
|
|
|
allow_can = False;
|
|
|
|
read_events(False);
|
|
|
|
allow_can = True;
|
|
|
|
if (GS_pid < 0) break; /* if timeout occurred */
|
|
|
|
#else
|
|
|
|
qt_processEvents();
|
|
|
|
#endif /* KDVI */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAS_SIGIO
|
|
|
|
(void) fcntl(ConnectionNumber(DISP), F_SETFL,
|
|
|
|
fcntl(ConnectionNumber(DISP), F_GETFL, 0) | FASYNC);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* put back generic handler for SIGPIPE */
|
|
|
|
#ifdef _POSIX_SOURCE
|
|
|
|
(void) sigaction(SIGPIPE, &orig, (struct sigaction *) NULL);
|
|
|
|
#else
|
|
|
|
(void) signal(SIGPIPE, orig);
|
|
|
|
#endif
|
|
|
|
GS_sending = False;
|
|
|
|
|
|
|
|
if (sigpipe_error) {
|
|
|
|
Fputs("ghostscript died unexpectedly.\n", stderr);
|
|
|
|
destroy_gs();
|
|
|
|
draw_bbox();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GS_pending_int) {
|
|
|
|
GS_pending_int = False;
|
|
|
|
interrupt_gs();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for acknowledgement from gs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
waitack(waittime)
|
|
|
|
int waittime;
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
struct timeval tv2;
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
struct timeval *timeout = (struct timeval *) NULL;
|
|
|
|
#else
|
|
|
|
int timeout = -1;
|
|
|
|
int retval;
|
|
|
|
#endif
|
|
|
|
#if HAS_SIGIO
|
|
|
|
int oldflags;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (GS_pending == 0) return;
|
|
|
|
#if HAS_SIGIO
|
|
|
|
oldflags = fcntl(ConnectionNumber(DISP), F_GETFL, 0);
|
|
|
|
(void) fcntl(ConnectionNumber(DISP), F_SETFL, oldflags & ~FASYNC);
|
|
|
|
#endif
|
|
|
|
if (waittime != 0) {
|
|
|
|
(void) gettimeofday(&tv, (struct timezone *) NULL);
|
|
|
|
tv.tv_sec += waittime;
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
timeout = &tv2;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
#endif
|
|
|
|
while (GS_pending > 0) {
|
|
|
|
if (waittime != 0) {
|
|
|
|
(void) gettimeofday(&tv2, (struct timezone *) NULL);
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
if (timercmp(&tv2, &tv, >=)) {
|
|
|
|
destroy_gs();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
tv2.tv_sec = tv.tv_sec - tv2.tv_sec;
|
|
|
|
tv2.tv_usec = tv.tv_usec + 1000000 - tv2.tv_usec;
|
|
|
|
if (tv2.tv_usec >= 1000000) tv2.tv_usec -= 1000000;
|
|
|
|
else --tv2.tv_sec;
|
|
|
|
#else
|
|
|
|
timeout = 1000 * (int) (tv.tv_sec - tv2.tv_sec)
|
|
|
|
+ ((long) tv.tv_usec - (long) tv2.tv_usec) / 1000;
|
|
|
|
if (timeout <= 0) {
|
|
|
|
destroy_gs();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
FD_SET(ConnectionNumber(DISP), &readfds);
|
|
|
|
FD_SET(GS_out, &readfds);
|
|
|
|
if (select(numfds, &readfds, (fd_set *) NULL, (fd_set *) NULL,
|
|
|
|
timeout) < 0 && errno != EINTR) {
|
|
|
|
perror("select (gs_waitack)");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#else /* STREAMSCONN */
|
|
|
|
for (;;) {
|
|
|
|
retval = poll(fds + 1, XtNumber(fds) - 1, timeout);
|
|
|
|
if (retval >= 0 || errno != EAGAIN) break;
|
|
|
|
}
|
|
|
|
if (retval < 0) {
|
|
|
|
perror("poll (gs_waitack)");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif /* STREAMSCONN */
|
|
|
|
|
|
|
|
|
|
|
|
if (XDVI_ISSET(GS_out, &readfds, 1))
|
|
|
|
read_from_gs();
|
|
|
|
if (XDVI_ISSET(ConnectionNumber(DISP), &readfds, 2)) {
|
|
|
|
#ifndef KDVI
|
|
|
|
allow_can = False;
|
|
|
|
read_events(False);
|
|
|
|
allow_can = True;
|
|
|
|
#else
|
|
|
|
qt_processEvents();
|
|
|
|
#endif /* KDVI */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if HAS_SIGIO
|
|
|
|
(void) fcntl(ConnectionNumber(DISP), F_SETFL, oldflags);
|
|
|
|
#endif
|
|
|
|
/* If you bail out here, change the call in interrupt_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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Boolean
|
|
|
|
initGS()
|
|
|
|
{
|
|
|
|
char buf[100];
|
|
|
|
/*
|
|
|
|
* 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 _Xconst 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$run \
|
|
|
|
clear countdictstack xdvi$dslen sub {end} repeat } \
|
|
|
|
ifelse \
|
|
|
|
{(%%xdvimark) currentfile =string {readline} stopped \
|
|
|
|
{clear} {pop eq {exit} if} ifelse }loop \
|
|
|
|
flushpage xdvi$ack print flush \
|
|
|
|
}loop\nH";
|
|
|
|
static _Xconst char str2[] = "matrix currentmatrix \
|
|
|
|
dup dup 4 get round 4 exch put \
|
|
|
|
dup dup 5 get round 5 exch put setmatrix\n\
|
|
|
|
stop\n%%xdvimark\n";
|
|
|
|
|
|
|
|
#ifndef KDVI
|
|
|
|
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",
|
|
|
|
None, /* bpixmap */
|
|
|
|
XtPageOrientationPortrait,
|
|
|
|
GS_page_w = page_w, GS_page_h = page_h);
|
|
|
|
XChangeProperty(DISP, mane.win, gs_atom, XA_STRING, 8,
|
|
|
|
PropModeReplace, (unsigned char *) buf, strlen(buf));
|
|
|
|
|
|
|
|
gs_colors_atom = XInternAtom(DISP, "GHOSTVIEW_COLORS", False);
|
|
|
|
Sprintf(buf, "%s %ld %ld", "Color", fore_Pixel, back_Pixel);
|
|
|
|
XChangeProperty(DISP, mane.win, gs_colors_atom, XA_STRING, 8,
|
|
|
|
PropModeReplace, (unsigned char *) buf, strlen(buf));
|
|
|
|
|
|
|
|
XSync(DISP, False); /* update the window */
|
|
|
|
|
|
|
|
if (xpipe(std_in) != 0 || xpipe(std_out) != 0) {
|
|
|
|
perror("pipe");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
Fflush(stderr); /* to avoid double flushing */
|
|
|
|
GS_pid = vfork();
|
|
|
|
if (GS_pid == 0) { /* child */
|
|
|
|
Sprintf(buf, "%ld", mane.win);
|
|
|
|
setenv("GHOSTVIEW", buf, True);
|
|
|
|
setenv("DISPLAY", XDisplayString(DISP), True);
|
|
|
|
(void) close(std_in[1]);
|
|
|
|
(void) dup2(std_in[0], 0);
|
|
|
|
(void) close(std_in[0]);
|
|
|
|
(void) close(std_out[0]);
|
|
|
|
(void) dup2(std_out[1], 1);
|
|
|
|
(void) dup2(std_out[1], 2);
|
|
|
|
(void) close(std_out[1]);
|
|
|
|
(void) execvp(argv[0], argv);
|
|
|
|
Fprintf(stderr, "Execvp of %s failed.\n", argv[0]);
|
|
|
|
Fflush(stderr);
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
#else /* KDVI */
|
|
|
|
extern int useAlpha;
|
|
|
|
extern Window mainwin;
|
|
|
|
|
|
|
|
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",
|
|
|
|
None, /* bpixmap */
|
|
|
|
XtPageOrientationPortrait,
|
|
|
|
GS_page_w = page_w, GS_page_h = page_h);
|
|
|
|
XChangeProperty(DISP, mainwin, gs_atom, XA_STRING, 8,
|
|
|
|
PropModeReplace, (unsigned char *) buf, strlen(buf));
|
|
|
|
|
|
|
|
gs_colors_atom = XInternAtom(DISP, "GHOSTVIEW_COLORS", False);
|
|
|
|
Sprintf(buf, "%s %ld %ld", "Color", fore_Pixel, back_Pixel);
|
|
|
|
XChangeProperty(DISP, mainwin, gs_colors_atom, XA_STRING, 8,
|
|
|
|
PropModeReplace, (unsigned char *) buf, strlen(buf));
|
|
|
|
|
|
|
|
XSync(DISP, False); /* update the window */
|
|
|
|
|
|
|
|
if (xpipe(std_in) != 0 || xpipe(std_out) != 0) {
|
|
|
|
perror("pipe");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
Fflush(stderr); /* to avoid double flushing */
|
|
|
|
GS_pid = vfork();
|
|
|
|
if (GS_pid == 0) { /* child */
|
|
|
|
Sprintf(buf, "%ld %ld", mainwin, mane.win);
|
|
|
|
setenv("GHOSTVIEW", buf, True);
|
|
|
|
setenv("DISPLAY", XDisplayString(DISP), True);
|
|
|
|
(void) close(std_in[1]);
|
|
|
|
(void) dup2(std_in[0], 0);
|
|
|
|
(void) close(std_in[0]);
|
|
|
|
(void) close(std_out[0]);
|
|
|
|
(void) dup2(std_out[1], 1);
|
|
|
|
(void) dup2(std_out[1], 2);
|
|
|
|
(void) close(std_out[1]);
|
|
|
|
argv[1] = useAlpha ? "-sDEVICE=x11alpha" : "-sDEVICE=x11";
|
|
|
|
(void) execvp(argv[0], argv);
|
|
|
|
Fprintf(stderr, "Execvp of %s failed.\n", argv[0]);
|
|
|
|
Fflush(stderr);
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
#endif /* KDVI */
|
|
|
|
if (GS_pid == -1) { /* error */
|
|
|
|
perror("vfork");
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
(void) close(std_in[0]);
|
|
|
|
(void) close(std_out[1]);
|
|
|
|
|
|
|
|
/* Set std_in for non-blocking I/O */
|
|
|
|
(void) fcntl(std_in[1], F_SETFL,
|
|
|
|
fcntl(std_in[1], F_GETFL, 0) | O_NONBLOCK);
|
|
|
|
|
|
|
|
#ifdef _POSIX_SOURCE
|
|
|
|
sigpipe_handler_struct.sa_handler = gs_sigpipe_handler;
|
|
|
|
sigemptyset(&sigpipe_handler_struct.sa_mask);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef STREAMSCONN
|
|
|
|
numfds = ConnectionNumber(DISP);
|
|
|
|
if (numfds < std_in[1]) numfds = std_in[1];
|
|
|
|
if (numfds < std_out[0]) numfds = std_out[0];
|
|
|
|
++numfds;
|
|
|
|
#else /* STREAMSCONN */
|
|
|
|
fds[0].fd = std_in[1];
|
|
|
|
fds[1].fd = std_out[0];
|
|
|
|
fds[2].fd = ConnectionNumber(DISP);
|
|
|
|
#endif /* STREAMSCONN */
|
|
|
|
|
|
|
|
psp = gs_procs;
|
|
|
|
GS_active = GS_sending = GS_pending_int = False;
|
|
|
|
GS_pending = 1;
|
|
|
|
GS_mag = GS_shrink = -1;
|
|
|
|
|
|
|
|
send(str1, sizeof(str1) - 1);
|
|
|
|
send(psheader, psheaderlen);
|
|
|
|
Sprintf(buf, "[1 0 0 -1 0 %d] concat\n", page_h);
|
|
|
|
send(buf, strlen(buf));
|
|
|
|
send(str2, sizeof(str2) - 1);
|
|
|
|
waitack(0);
|
|
|
|
|
|
|
|
if (GS_pid < 0) { /* if something happened */
|
|
|
|
destroy_gs();
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
if (!postscript) toggle_gs(); /* if we got a 'v' already */
|
|
|
|
else {
|
|
|
|
#ifndef KDVI
|
|
|
|
canit = True; /* ||| redraw the page */
|
|
|
|
longjmp(canit_env, 1);
|
|
|
|
#endif /* KDVI */
|
|
|
|
}
|
|
|
|
return True;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
toggle_gs()
|
|
|
|
{
|
|
|
|
if (debug & DBG_PS) Puts("Toggling GS on or off");
|
|
|
|
if (postscript) psp.drawbegin = drawbegin_gs;
|
|
|
|
else {
|
|
|
|
interrupt_gs();
|
|
|
|
psp.drawbegin = drawbegin_none;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
destroy_gs()
|
|
|
|
{
|
|
|
|
if (debug & DBG_PS) Puts("Destroying GS process");
|
|
|
|
if (linepos > line) {
|
|
|
|
*linepos = '\0';
|
|
|
|
Printf("gs: %s\n", line);
|
|
|
|
linepos = line;
|
|
|
|
}
|
|
|
|
if (GS_pid >= 0) {
|
|
|
|
if (kill(GS_pid, SIGKILL) < 0 && errno != ESRCH)
|
|
|
|
perror("destroy_gs");
|
|
|
|
GS_pid = -1;
|
|
|
|
}
|
|
|
|
(void) close(GS_in);
|
|
|
|
(void) close(GS_out);
|
|
|
|
GS_active = GS_sending = GS_pending_int = False;
|
|
|
|
GS_pending = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
interrupt_gs()
|
|
|
|
{
|
|
|
|
static _Xconst char str[] = " stop\n%%xdvimark\n";
|
|
|
|
|
|
|
|
if (debug & DBG_PS) Puts("Running interrupt_gs()");
|
|
|
|
if (GS_sending) GS_pending_int = True;
|
|
|
|
else {
|
|
|
|
if (GS_active) {
|
|
|
|
/*
|
|
|
|
* ||| 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.
|
|
|
|
*/
|
|
|
|
send(str, sizeof(str) - 1);
|
|
|
|
GS_active = False;
|
|
|
|
}
|
|
|
|
psp.interrupt = NullProc; /* prevent deep recursion in waitack */
|
|
|
|
waitack(5);
|
|
|
|
psp.interrupt = interrupt_gs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
endpage_gs()
|
|
|
|
{
|
|
|
|
static _Xconst char str[] = "stop\n%%xdvimark\n";
|
|
|
|
|
|
|
|
if (debug & DBG_PS) Puts("Running endpage_gs()");
|
|
|
|
if (GS_active) {
|
|
|
|
send(str, sizeof(str) - 1);
|
|
|
|
GS_active = False;
|
|
|
|
waitack(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drawbegin_gs(xul, yul, cp)
|
|
|
|
int xul, yul;
|
|
|
|
char *cp;
|
|
|
|
{
|
|
|
|
char buf[100];
|
|
|
|
static _Xconst char str[] = " TeXDict begin\n";
|
|
|
|
|
|
|
|
/* check page_w and page_h to see that they haven't increased */
|
|
|
|
if (page_w > GS_page_w || page_h > GS_page_h) {
|
|
|
|
/* It would be nice if we could just resize the window, but I
|
|
|
|
* don't see a convenient way to do that. */
|
|
|
|
destroy_gs();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GS_pid < 0)
|
|
|
|
(void) initGS();
|
|
|
|
|
|
|
|
if (!GS_active) {
|
|
|
|
if (magnification != GS_mag) {
|
|
|
|
Sprintf(buf, "H TeXDict begin /DVImag %d 1000 div def \
|
|
|
|
end stop\n%%%%xdvimark\n",
|
|
|
|
GS_mag = magnification);
|
|
|
|
send(buf, strlen(buf));
|
|
|
|
++GS_pending;
|
|
|
|
}
|
|
|
|
if (mane.shrinkfactor != GS_shrink) {
|
|
|
|
Sprintf(buf,
|
|
|
|
"H TeXDict begin %d %d div dup \
|
|
|
|
/Resolution X /VResolution X \
|
|
|
|
end stop\n%%%%xdvimark\n",
|
|
|
|
pixels_per_inch, GS_shrink = mane.shrinkfactor);
|
|
|
|
send(buf, strlen(buf));
|
|
|
|
++GS_pending;
|
|
|
|
}
|
|
|
|
send(str, sizeof(str) - 1);
|
|
|
|
GS_active = True;
|
|
|
|
++GS_pending;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This allows the X side to clear the page */
|
|
|
|
XSync(DISP, False);
|
|
|
|
|
|
|
|
Sprintf(buf, "%d %d moveto\n", xul, yul);
|
|
|
|
send(buf, strlen(buf));
|
|
|
|
if (debug & DBG_PS)
|
|
|
|
Printf("drawbegin at %d,%d: sending `%s'\n", xul, yul, cp);
|
|
|
|
send(cp, strlen(cp));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drawraw_gs(cp)
|
|
|
|
char *cp;
|
|
|
|
{
|
|
|
|
int len = strlen(cp);
|
|
|
|
|
|
|
|
if (!GS_active)
|
|
|
|
return;
|
|
|
|
if (debug & DBG_PS) Printf("raw ps sent to context: %s\n", cp);
|
|
|
|
cp[len] = '\n';
|
|
|
|
send(cp, len + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drawfile_gs(cp)
|
|
|
|
char *cp;
|
|
|
|
{
|
|
|
|
char buf[PATH_MAX + 7];
|
|
|
|
|
|
|
|
if (!GS_active)
|
|
|
|
return;
|
|
|
|
if (debug & DBG_PS) Printf("printing file %s\n", cp);
|
|
|
|
Sprintf(buf, "(%s)run\n", cp);
|
|
|
|
send(buf, strlen(buf));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
drawend_gs(cp)
|
|
|
|
char *cp;
|
|
|
|
{
|
|
|
|
if (!GS_active)
|
|
|
|
return;
|
|
|
|
if (debug & DBG_PS) Printf("end ps: %s\n", cp);
|
|
|
|
send(cp, strlen(cp));
|
|
|
|
send("\n", 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* PS_GS */
|