Back to index

tetex-bin  3.0
psgs.c
Go to the documentation of this file.
00001 /*========================================================================*\
00002 
00003 Copyright (c) 1994-2004  Paul Vojta
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to
00007 deal in the Software without restriction, including without limitation the
00008 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00009 sell copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018 PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00019 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00020 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00021 OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 \*========================================================================*/
00024 
00025 #ifdef PS_GS  /* entire file */
00026 
00027 
00028 #include <setjmp.h>
00029 #include <X11/Xatom.h>
00030 #include <sys/time.h>       /* for timeval */
00031 
00032 #include <signal.h>
00033 
00034 /* Condition for retrying a write */
00035 #include <errno.h>
00036 
00037 #include "xdvi-config.h"
00038 #include "xdvi.h"
00039 #include "psgs.h"
00040 
00041 #include "kpathsea/c-pathmx.h"
00042 #include "dvi-init.h"
00043 #include "dvi-draw.h"
00044 #include "events.h"
00045 #include "statusline.h"
00046 #include "special.h"
00047 #include "util.h"
00048 
00049 
00050 #ifdef X_NOT_STDC_ENV
00051 extern int    errno;
00052 #endif
00053 
00054 #ifdef EWOULDBLOCK
00055 # ifdef       EAGAIN
00056 #  define AGAIN_CONDITION   (errno == EWOULDBLOCK || errno == EAGAIN)
00057 # else
00058 #  define AGAIN_CONDITION   (errno == EWOULDBLOCK)
00059 # endif
00060 #else
00061 # ifdef       EAGAIN
00062 #  define AGAIN_CONDITION   (errno == EAGAIN)
00063 # endif
00064 #endif
00065 
00066 #if HAVE_POLL
00067 # include <poll.h>
00068 # define XIO_IN POLLIN
00069 # define XIO_OUT POLLOUT
00070 #else
00071 # define XIO_IN 1
00072 # define XIO_OUT 2
00073 #endif
00074 
00075 #ifdef MOTIF
00076 # include <Xm/Xm.h>
00077 #endif
00078 
00079 extern const char psheader[];
00080 extern unsigned psheaderlen;
00081 
00082 Boolean gs_postpone_prescan = False;
00083 
00084 /* global procedures (besides initGS) */
00085 
00086 static void toggle_gs(int flag);
00087 static void destroy_gs(void);
00088 static void interrupt_gs(void);
00089 static void endpage_gs(void);
00090 static void drawbegin_gs(int, int, const char *);
00091 static void drawbegin_gs_box(int, int, const char *);
00092 void drawraw_gs(const char *);
00093 static void drawfile_gs(const char *, FILE *);
00094 static void drawend_gs(const char *);
00095 static void beginheader_gs(void);
00096 static void endheader_gs(void);
00097 static void newdoc_gs(void);
00098 
00099 static struct psprocs gs_procs = {
00100     /* toggle */ toggle_gs,
00101     /* destroy */ destroy_gs,
00102     /* interrupt */ interrupt_gs,
00103     /* endpage */ endpage_gs,
00104     /* drawbegin */ drawbegin_gs,
00105     /* drawraw */ drawraw_gs,
00106     /* drawfile */ drawfile_gs,
00107     /* drawend */ drawend_gs,
00108     /* beginheader */ beginheader_gs,
00109     /* endheader */ endheader_gs,
00110     /* newdoc */ newdoc_gs
00111 };
00112 
00113 static int std_io[2];
00114 Pixmap bpixmap;
00115 
00116 #define       GS_fd  (std_io[0])
00117 
00118               /* some arguments are filled in later */
00119 static char arg4[] = "-dDEVICEWIDTH=xxxxxxxxxx";
00120 static char arg5[] = "-dDEVICEHEIGHT=xxxxxxxxxx";
00121 
00122 static const char *argv[] = {
00123     NULL, NULL, "-dNOPAUSE", "-q", arg4, arg5,
00124     "-dDEVICEXRESOLUTION=72",
00125     "-dDEVICEYRESOLUTION=72",
00126     "-dNOSAFER", "-dNOEPS", NULL, NULL, NULL, NULL, NULL, NULL
00127 };
00128 
00129 static unsigned int GS_page_w;     /* how big our current page is */
00130 static unsigned int GS_page_h;
00131 static Boolean GS_alpha;    /* remember if we are using the alpha driver */
00132 static unsigned long GS_mag;       /* magnification currently in use */
00133 static int GS_shrink;              /* shrink factor currently in use */
00134 static Boolean GS_active;   /* if we've started a page yet */
00135 static int GS_pending;             /* number of ack's we're expecting */
00136 static char GS_outb[257];   /* circular output buffer */
00137 static char *GS_outb_in;    /* next position in output buffer */
00138 static char *GS_outb_out;   /* next byte to come out of buffer */
00139 #define       GS_outb_limit (GS_outb + sizeof GS_outb)  /* last+1 byte */
00140 static int GS_write_ack;    /* flag to set when done writing */
00141 static Boolean GS_in_header;       /* if we're sending a header */
00142 static Boolean GS_in_doc;   /* if we've sent header information */
00143 static int GS_ev_mask;             /* events for which we'll stop */
00144 static int GS_die_ack = 0;  /* flags to set when GS dies */
00145 static Boolean GS_timer_set;       /* if there's a timer set */
00146 static Boolean GS_old;             /* if we're using gs 2.xx */
00147 
00148 #define       GS_MASK_NORMAL       EV_GE_NEWPAGE
00149 #define       GS_MASK_HEADER       EV_GE_PS_TOGGLE
00150 #define       GS_MASK_INIT  (EV_GE_TERM | EV_PS_TOGGLE)
00151 
00152 static Atom gs_atom;
00153 static Atom gs_colors_atom;
00154 
00155 #define       Landscape     90
00156 
00157 #define       LINELEN       81
00158 static char line[LINELEN + 1];
00159 static char *linepos = line;
00160 static char ackstr[] = "\347\310\376";
00161 static char oldstr[] = "\347\310\375";
00162 
00163 static void gs_died(int, struct xchild *);
00164 
00165 static char *read_from_gs(int);
00166 static void write_to_gs(int);
00167 
00168 static struct xio    gs_xio        = {NULL, 0, XIO_IN,
00169 #if HAVE_POLL
00170     NULL,
00171 #endif
00172     read_from_gs, write_to_gs
00173 };
00174 
00175 static struct xchild gs_child = { NULL, 0, True, NULL, NULL, NULL, gs_died };
00176 #define       GS_pid        (gs_child.pid)
00177 
00178 /* It seems that XtTimers don't work reliably with xdvi's event treatment;
00179    so use custom timers for the time being. Search for TIMER_PROBLEM_FIXED
00180    to locate the relevant code parts.
00181 */
00182 
00183 #if TIMER_PROBLEM_FIXED
00184 static XtIntervalId gs_timeout_id = 0;
00185 static void gs_timer_proc(XtPointer client_data, XtIntervalId * id);
00186 static struct xtimer gs_timer      = {NULL, {0, 0}, gs_timer_proc};
00187 #else
00188 static void gs_alarm(struct xtimer *timer);
00189 static struct xtimer gs_timer = {NULL, {0, 0}, XTM_DEFAULT, gs_alarm
00190 #if XDVI_XT_TIMER_HACK
00191                                 , NULL, NULL
00192 #endif
00193 };
00194 #endif
00195 
00196 
00197 static void
00198 showto(char *q)
00199 {
00200     char *p = line;
00201     char *p1;
00202 
00203     while (p < q) {
00204        p1 = memchr(p, '\n', q - p);
00205        if (p1 == NULL)
00206            p1 = q;
00207        *p1 = '\0';
00208        printf("gs: %s\n", p);
00209        p = p1 + 1;
00210     }
00211 }
00212 
00213 static char *
00214 read_from_gs(int fd)
00215 {
00216     int bytes;
00217     char *line_end = NULL;
00218     char *p;
00219 
00220     for (;;) {
00221 
00222        bytes = read(fd, linepos, line + LINELEN - linepos);
00223        if (bytes < 0) {
00224            if (AGAIN_CONDITION)
00225               break;
00226            perror("xdvik: read_from_gs");
00227            break;
00228        }
00229        line_end = linepos + bytes;
00230 
00231        if (bytes == 0) {
00232            if (GS_pid != 0)
00233               XDVI_WARNING((stderr, "Read_from_gs returned 0 bytes"));
00234            break;
00235        }
00236 
00237        /* Check for ack strings */
00238        for (p = line; p < line_end - 2; ++p) {
00239            p = memchr(p, '\347', line_end - p - 2);
00240            if (p == NULL) break;
00241            if (memcmp(p, ackstr, 3) == 0) {
00242               --GS_pending;
00243               if (GS_pending == 0)
00244                   globals.ev.flags |= EV_ACK;
00245               if (globals.debug & DBG_PS)
00246                   printf("Got GS ack; %d pending.\n", GS_pending);
00247            } else if (memcmp(p, oldstr, 3) == 0) {
00248               if (globals.debug & DBG_PS)
00249                   puts("Using old GS version.");
00250               GS_old = True;
00251            } else
00252               continue;
00253 
00254            showto(p);
00255            p += 3;
00256            memmove(line, p, line_end - p);
00257            line_end -= p - line;
00258            linepos = p = line;
00259            --p;
00260        }
00261        *line_end = '\0';
00262        p = rindex(linepos, '\n');
00263        if (p != NULL) {
00264            ++p;
00265            showto(p);
00266            memmove(line, p, line_end - p);
00267            line_end -= p - line;
00268        }
00269        linepos = line_end;
00270        /*
00271         * Normally we'd hold text until a newline character, but the
00272         * buffer is full.  So we flush it, being careful not to cut up an
00273         * ack string.
00274         */
00275        if (linepos >= line + LINELEN) {
00276            p = line + LINELEN;
00277            if ((*--p != '\347' && *--p != '\347' && *--p != '\347')
00278                   || (memcmp(p, ackstr, line + LINELEN - p) != 0
00279                      && memcmp(p, oldstr, line + LINELEN - p) != 0))
00280               p = line + LINELEN;
00281            *p = '\0';
00282            printf("gs: %s\n", line);
00283            *p = '\347';
00284            linepos = line;
00285            while (p < line + LINELEN)
00286               *linepos++ = *p++;
00287        }
00288     }
00289     return line_end; /* dummy */
00290 }
00291 
00292 static void
00293 write_to_gs(int fd)
00294 {
00295     char *send_end;
00296     int       bytes;
00297 
00298     UNUSED(fd);
00299     
00300     for (;;) {
00301        send_end = GS_outb_in;
00302        if (send_end < GS_outb_out)
00303            send_end = GS_outb_limit;
00304        bytes = write(GS_fd, GS_outb_out, send_end - GS_outb_out);
00305 
00306        if (bytes < 0) {
00307            if (AGAIN_CONDITION)
00308               break;
00309            perror("xdvik: write_to_gs");
00310            break;
00311        }
00312 
00313        GS_outb_out += bytes;
00314        if (GS_outb_out == GS_outb_limit)
00315            GS_outb_out = GS_outb;
00316        if (GS_outb_out == GS_outb_in) {   /* if buffer is empty */
00317            gs_xio.xio_events = XIO_IN;
00318 #if HAVE_POLL
00319            if (gs_xio.pfd != NULL) /* write_to_gs is called directly */
00320               gs_xio.pfd->events = XIO_IN;
00321 #endif
00322            break;
00323        }
00324     }
00325     globals.ev.flags |= GS_write_ack;
00326     GS_write_ack = 0;
00327 }
00328 
00329 /*
00330  *     Main routine for writing to the GS interpreter.
00331  */
00332 
00333 static void
00334 gs_send(const char *cp, size_t len)
00335 {
00336     const char *cp_end = cp + len;
00337     char *send_end;
00338     size_t bytes;
00339     char *old_out;
00340     Boolean interrupting;
00341 
00342     if (GS_pid == 0 || (globals.ev.flags & GS_ev_mask))
00343        return;
00344 
00345     /*
00346      * Because cp might reside on the stack, don't return until we've
00347      * copied all of it to our circular output buffer.
00348      * Note that GS_outb_out == GS_outb_in means that the buffer is empty.
00349      */
00350 
00351     GS_timer_set = interrupting = False;
00352     for (;;) {
00353        send_end = GS_outb_out;
00354        if (send_end == GS_outb)
00355            send_end = GS_outb_limit;
00356        --send_end;
00357        if (send_end < GS_outb_in)
00358            send_end = GS_outb_limit;
00359        bytes = send_end - GS_outb_in;
00360        if (bytes > 0) {
00361            if (bytes >= (unsigned)(cp_end - cp))
00362               bytes = cp_end - cp;
00363            memcpy(GS_outb_in, cp, bytes);
00364            cp += bytes;
00365            GS_outb_in += bytes;
00366            if (GS_outb_in == GS_outb_limit)
00367               GS_outb_in = GS_outb;
00368            if (cp < cp_end)
00369               continue;
00370        }
00371 
00372        /* The buffer is now full --or-- we've run out of data */
00373        old_out = GS_outb_out;
00374        if (!(gs_xio.xio_events & XIO_OUT)) {     /* restart output */
00375            gs_xio.xio_events = XIO_IN | XIO_OUT;
00376 #if HAVE_POLL
00377            if (gs_xio.pfd != NULL)
00378               gs_xio.pfd->events = POLLIN | POLLOUT;
00379 #endif
00380            write_to_gs(GS_fd);
00381            if (GS_outb_out != old_out) {
00382               if (cp == cp_end)
00383                   break;
00384               else
00385                   continue;
00386            }
00387        }
00388 
00389        if (cp == cp_end)
00390            break;
00391 
00392        GS_die_ack = GS_write_ack = EV_ACK;
00393        for (;;) {    /* loop because there may be stray ACKs */
00394            if (!interrupting) {
00395               (void) read_events(GS_ev_mask | EV_ACK);
00396               globals.ev.flags &= ~EV_ACK;
00397 
00398               if (GS_pid == 0) {   /* if GS died */
00399                   GS_die_ack = 0;
00400                   return;
00401               }
00402               
00403               if (GS_outb_out != old_out) /* if more room in buffer */
00404                   break;
00405 
00406               if (globals.ev.flags & GS_ev_mask) {      /* if a serious event */
00407                   if (globals.debug & DBG_PS)
00408                      puts("Setting timeout in gs_send()");
00409 
00410                   set_timer(&gs_timer, resource.gs_timeout);
00411                   GS_timer_set = interrupting = True;
00412               }
00413            }
00414            else {
00415               (void) read_events(EV_GE_TERM | EV_PS_TOGGLE | EV_ACK);
00416               globals.ev.flags &= ~EV_ACK;
00417 
00418               if (GS_outb_out != old_out) /* if more room in buffer */
00419                   break;
00420 
00421               if (GS_timer_set)    /* if timer still set */
00422                   cancel_timer(&gs_timer);
00423 
00424               destroy_gs();
00425               GS_die_ack = 0;
00426               return;
00427            }
00428        }
00429     }
00430 
00431     if (GS_timer_set)       /* if timer still set */
00432        cancel_timer(&gs_timer);
00433 
00434     GS_die_ack = GS_write_ack = 0;
00435 }
00436 
00437 static void
00438 #if TIMER_PROBLEM_FIXED
00439 gs_timer_proc(XtPointer client_data, XtIntervalId * id)
00440 #else
00441 gs_alarm(struct xtimer *timer)
00442 #endif
00443 {
00444     UNUSED(timer);
00445     
00446     if (globals.debug & DBG_PS)
00447        puts("GS timeout expired");
00448 
00449     globals.ev.flags |= EV_ACK;
00450     GS_timer_set = False;
00451 #if TIMER_PROBLEM_FIXED
00452     gs_timeout_id = 0;
00453 #endif
00454 }
00455 
00456 /*
00457  *     Wait for acknowledgement from GS.
00458  */
00459 
00460 static void
00461 waitack(void)
00462 {
00463     if (GS_pending == 0) {
00464        globals.ev.flags &= ~EV_ACK;
00465        return;
00466     }
00467 
00468     GS_die_ack = EV_ACK;
00469     
00470     for (;;) {       /* loop because there might be stray ACKs. */
00471 /*     fprintf(stderr, "looping for stray ACKs, page %d\n", current_page); */
00472        (void) read_events(EV_GE_ACK);
00473        globals.ev.flags &= ~EV_ACK;
00474 
00475        if (GS_pending == 0) {
00476            GS_die_ack = 0;
00477            return;
00478        }
00479        if (globals.ev.flags & EV_GE_ACK)
00480            break;
00481     }
00482 
00483     if (globals.debug & DBG_PS)
00484        puts("Setting timeout in waitack()");
00485 
00486 #if TIMER_PROBLEM_FIXED
00487     if (gs_timeout_id)
00488        XtRemoveTimeOut(gs_timeout_id);
00489     gs_timeout_id = XtAppAddTimeOut(app, resource.gs_timeout,
00490                                 gs_timer_proc, (XtPointer) NULL);
00491 #else
00492     set_timer(&gs_timer, resource.gs_timeout);
00493 #endif
00494     GS_timer_set = True;
00495 
00496     (void) read_events(EV_GE_TERM | EV_PS_TOGGLE | EV_ACK);
00497     globals.ev.flags &= ~EV_ACK;
00498 
00499     if (GS_timer_set) {
00500 #if TIMER_PROBLEM_FIXED
00501        XtRemoveTimeOut(gs_timeout_id);
00502        gs_timeout_id = 0;
00503 #else
00504        cancel_timer(&gs_timer);
00505 #endif
00506     }
00507     GS_die_ack = 0;
00508 
00509     if (GS_pending > 0)
00510        destroy_gs();
00511 }
00512 
00513 
00514 /*
00515  *     Fork a process to run ghostscript.  This is done using the
00516  *     x11 device (which needs to be compiled in).  Normally the x11
00517  *     device uses ClientMessage events to communicate with the calling
00518  *     program, but we don't do this.  The reason for using the ClientMessage
00519  *     events is that otherwise ghostview doesn't know when a non-conforming
00520  *     postscript program calls showpage.   That doesn't affect us here,
00521  *     since in fact we disable showpage.
00522  *
00523  *     SAFER mode is handled by providing both the -dNOSAFER and -dSAFER
00524  *     options. Ghostscript versions 7.04 and earlier ignore -dNOSAFER and use
00525  *     -dSAFER; the code in strsafe is ignored since .locksafe is not present.
00526  *     In versions 7.04 and higher, -dNOSAFER overrides -dSAFER; SAFER mode is
00527  *     optionally turned on by sending the strsafe string.  It is possible in some
00528  *     versions of gs prior to 7.04 to use -dDELAYSAFER instead of -dNOSAFER, but
00529  *     there's no point in doing that since .locksafe is not defined in those
00530  *     versions.  I don't know where 7.03 fits in on all of this (but it works with
00531  *     that version as well).
00532  */
00533 
00534 Boolean
00535 initGS(void)
00536 {
00537     char buf[100];
00538     static Boolean did_putenv = False;
00539     /*
00540      * This string reads chunks (delimited by %%xdvimark).
00541      * The first character of a chunk tells whether a given chunk
00542      * is to be done within save/restore or not.
00543      * The `H' at the end tells it that the first group is a
00544      * header; i.e., no save/restore.
00545      * `execute' is unique to ghostscript.
00546      */
00547 
00548     static const char strsafe[] =
00549        "{ << /PermitFileReading [ (*) ] /PermitFileWriting [ ] /PermitFileControl [ ] "
00550        ">> setuserparams .locksafe "
00551        "} stopped pop\n";
00552     static const char str1[] =
00553        "/xdvi$run {$error /newerror false put {currentfile cvx execute} stopped pop} "
00554        "def "
00555        "/xdvi$ack (\347\310\376) def "
00556        "/xdvi$dslen countdictstack def "
00557        "{currentfile read pop 72 eq "
00558        "{xdvi$run} "
00559        "{/xdvi$sav save def xdvi$run "
00560        "clear countdictstack xdvi$dslen sub {end} repeat xdvi$sav restore} "
00561        "ifelse "
00562        "{(%%xdvimark) currentfile =string {readline} stopped "
00563        "{clear} {pop eq {exit} if} ifelse }loop "
00564        "flushpage xdvi$ack print flush "
00565        "}loop\nH";
00566     
00567     static const char str2[] =
00568        "[0 1 1 0 0 0] concat\n"
00569        "revision 300 lt{(\347\310\375) print flush}if\n"
00570        "stop\n%%xdvimark\n";
00571     
00572     /*
00573       * If we're prescanning *before* setting up the widgets (to get the
00574       * page size, for example), then postpone starting up ghostscript.
00575       */
00576     if (mane.win == (Window)0) {
00577        if (globals.debug & DBG_PS)
00578            puts("Hit PS header in early prescan; postponing.");
00579        psp = no_ps_procs;
00580        gs_postpone_prescan = True;
00581        return True;
00582     }
00583     
00584     if (globals.debug & DBG_PS)
00585        puts("Running initGS ...");
00586     
00587     gs_atom = XInternAtom(DISP, "GHOSTVIEW", False);
00588     /* send bpixmap, orientation, bbox (in pixels), and h & v resolution */
00589     sprintf(buf, "%ld %d 0 0 %u %u 72 72",
00590            bpixmap,         /* bpixmap */
00591            Landscape,       /* orientation */
00592            GS_page_h = globals.page.h, GS_page_w = globals.page.w);
00593 
00594     XChangeProperty(DISP, mane.win, gs_atom, XA_STRING, 8,
00595                   PropModeReplace, (unsigned char *)buf, strlen(buf));
00596     GS_alpha = resource.gs_alpha;
00597 
00598     gs_colors_atom = XInternAtom(DISP, "GHOSTVIEW_COLORS", False);
00599     sprintf(buf, "%s %ld %ld", resource.gs_palette,
00600            color_data[0].pixel, color_data[1].pixel);
00601     
00602     XChangeProperty(DISP, mane.win, gs_colors_atom, XA_STRING, 8,
00603                   PropModeReplace, (unsigned char *)buf, strlen(buf));
00604 
00605     if (!did_putenv) {
00606        sprintf(buf, "%ld", mane.win);
00607        xputenv("GHOSTVIEW", buf);
00608        did_putenv = True;
00609     }
00610 
00611     XSync(DISP, False);     /* update the window */
00612 
00613     if (xpipe(std_io) != 0) {
00614        perror("[xdvik] pipe");
00615        return False;
00616     }
00617     fflush(stderr);  /* to avoid double flushing */
00618     GS_pid = vfork();
00619     if (GS_pid == 0) {      /* child */
00620        const char **argvp = argv + 10;
00621 
00622        argv[1] = resource.gs_alpha ? "-sDEVICE=x11alpha" : "-sDEVICE=x11";
00623        sprintf(arg4 + 14, "%u", GS_page_w);
00624        sprintf(arg5 + 15, "%u", GS_page_h);
00625        if (resource.gs_safer)
00626            *argvp++ = "-dSAFER";
00627 
00628 #if 0
00629        if (resource.gs_alpha) {
00630            *argvp++ = "-dGraphicsAlphaBits=4";
00631            *argvp++ = "-dTextAlphaBits=4";
00632            *argvp++ = "-dMaxBitmap=0";
00633        }
00634 #endif
00635        
00636        *argvp = "-";
00637        (void) close(std_io[0]);
00638        (void) dup2(std_io[1], 0);
00639        (void) dup2(std_io[1], 1);
00640        (void) dup2(std_io[1], 2);
00641        (void) close(std_io[1]);
00642 
00643        (void)execvp(argv[0] = resource.gs_path, (char *const *)argv);
00644        /*TODO: use fork_process here?? */
00645        XDVI_ERROR((stderr, "Execvp of %s failed: %s", argv[0], strerror(errno)));
00646        _exit(EXIT_FAILURE);
00647     }
00648 
00649     (void)close(std_io[1]);
00650 
00651     if (GS_pid == -1) {     /* error */
00652        GS_pid = 0;
00653        perror("[xdvik] vfork");
00654        (void)close(GS_fd);
00655 
00656        return False;
00657     }
00658 
00659     prep_fd(GS_fd, True);   /* Set file descriptor for non-blocking I/O */
00660 
00661     gs_child.name = xstrdup("gs");
00662     gs_child.io = &gs_xio;
00663     set_chld(&gs_child);
00664 
00665     psp = gs_procs;
00666     GS_active = False;
00667     GS_in_header = True;
00668     GS_pending = 1;
00669     GS_mag = GS_shrink = -1;
00670     gs_xio.fd = GS_fd;
00671     gs_xio.xio_events = XIO_IN;
00672     GS_write_ack = 0;
00673     GS_outb_in = GS_outb_out = GS_outb;
00674     set_io(&gs_xio);
00675     GS_ev_mask = GS_MASK_INIT;
00676     (void) signal(SIGPIPE, SIG_IGN);
00677     
00678     if (resource.gs_safer)
00679        gs_send(strsafe, (sizeof strsafe) - 1);
00680     gs_send(str1, (sizeof str1) - 1);
00681     gs_send(psheader, psheaderlen);
00682     gs_send(str2, (sizeof str2) - 1);
00683     waitack();
00684     GS_in_header = False;
00685     GS_ev_mask = GS_MASK_NORMAL;
00686 
00687     if (GS_pid == 0) {      /* if something happened */
00688        destroy_gs();
00689        return False;
00690     }
00691     if (resource.postscript == 0)
00692        toggle_gs(0); /* if we got a 'v' already */
00693     else {
00694        scanned_page = scanned_page_ps = scanned_page_reset;
00695        globals.ev.flags |= EV_NEWPAGE;    /* ||| redraw the page */
00696        longjmp(globals.ev.canit, 1);
00697     }
00698     return True;
00699 }
00700 
00701 static void
00702 toggle_gs(int flag)  /* this routine is callable from within read_events().  */
00703 {
00704     if (globals.debug & DBG_PS)
00705        fprintf(stdout, "Toggling GS to %d", flag);
00706 
00707     switch (flag) {
00708     case 0:
00709        psp.drawbegin = drawbegin_none;
00710        break;
00711     case 1:
00712        psp.drawbegin = drawbegin_gs;
00713        break;
00714     default:
00715        psp.drawbegin = drawbegin_gs_box;
00716        break;
00717     }
00718 }
00719 
00720 void
00721 gs_resume_prescan()
00722 {
00723     if (globals.debug & DBG_PS)
00724        puts("Resuming prescan");
00725     
00726     gs_postpone_prescan = False;
00727     if (!initGS())   /* this may not return */
00728        psp = no_ps_procs;
00729 }
00730 
00731 static void
00732 gs_died(int status, struct xchild *child)
00733 {
00734     UNUSED(status);
00735     if (globals.debug & DBG_PS)
00736        fprintf(stderr, "process %s died\n", child->name);
00737     GS_pid = 0;
00738     (child->io->read_proc)(child->io->fd);
00739     if (linepos > line) {
00740        *linepos = '\0';
00741        printf("%s: %s\n", child->name, line);
00742        linepos = line;
00743     }
00744     clear_io(child->io);
00745     (void)close(child->io->fd);
00746 
00747     scanned_page = scanned_page_ps = scanned_page_reset;
00748     GS_active = GS_in_doc = False;
00749     GS_pending = 0;
00750     globals.ev.flags |= GS_die_ack;
00751 }
00752 
00753 static void
00754 destroy_gs(void)
00755 {
00756     if (globals.debug & DBG_PS)
00757        puts("Destroying GS process");
00758     if (GS_pid != 0) {
00759        if (kill(GS_pid, SIGKILL) < 0 && errno != ESRCH)
00760            perror("xdvik: destroy_gs");
00761        GS_pid = 0;
00762        clear_chld(&gs_child);
00763        read_from_gs(GS_fd);
00764        if (linepos > line) {
00765            *linepos = '\0';
00766            printf("gs: %s\n", line);
00767            linepos = line;
00768        }
00769        clear_io(&gs_xio);
00770        (void) close(GS_fd);
00771 
00772        scanned_page = scanned_page_ps = scanned_page_reset;
00773     }
00774     GS_active = GS_in_doc = False;
00775     GS_pending = 0;
00776 }
00777 
00778 static void
00779 deactivate(void)
00780 {
00781     static const char str[] = " stop\n%%xdvimark\n";
00782     int       saved_mask;
00783 
00784     saved_mask = GS_ev_mask;
00785     GS_ev_mask = 0;
00786     gs_send(str, (sizeof str) - 1);
00787     GS_ev_mask = saved_mask;
00788 
00789     GS_active = False;
00790 }
00791 
00792 static void
00793 interrupt_gs(void)
00794 {
00795     if (globals.debug & DBG_PS)
00796        puts("Running interrupt_gs()");
00797     /*
00798       FIXME: added this special case, since otherwise EV_ACK might not get
00799       reset at all, and hang xdvi forever when scrolling quickly through
00800       a file that should be reloaded.
00801     */
00802     if (GS_pending == 0) {
00803 /*     fprintf(stderr, "+++++++++ 1st case; waitack: %d\n", globals.ev.flags & EV_ACK); */
00804        globals.ev.flags &= ~EV_ACK;
00805        return;
00806     }
00807     else if (GS_pending < 0) {
00808 /*     fprintf(stderr, "GS_pending: %d; waitack: %d\n", GS_pending, globals.ev.flags & EV_ACK); */
00809        return;       /* nothing to do */
00810     }
00811 
00812     /*
00813      * ||| what I'd really like to do here is cause gs to execute
00814      * the interrupt routine in errordict.  But so far (gs 2.6.1)
00815      * that has not been implemented in ghostscript.
00816      */
00817 
00818     if (GS_active)
00819        deactivate();
00820     waitack();
00821 }
00822 
00823 static void
00824 endpage_gs(void)
00825 {
00826     if (globals.debug & DBG_PS)
00827        puts("Running endpage_gs()");
00828     if (GS_active) {
00829        deactivate();
00830        waitack();
00831     }
00832 }
00833 
00834 /*
00835  *     Checks that the GS interpreter is running correctly.
00836  */
00837 
00838 static void
00839 checkgs(Boolean in_header)
00840 {
00841     char buf[150];
00842 
00843     /* For gs 2, we pretty much have to start over to enlarge the window. */
00844     if ((GS_old && (globals.page.w > GS_page_w || globals.page.h > GS_page_h))
00845        || GS_alpha != resource.gs_alpha)
00846        destroy_gs();
00847 
00848     if (GS_pid == 0)
00849        (void)initGS();
00850 
00851     if (!GS_active) {
00852        /* check whether page_w or page_h have increased */
00853        if (globals.page.w > GS_page_w || globals.page.h > GS_page_h) {
00854            if (globals.ev.flags & GS_ev_mask)
00855               longjmp(globals.ev.canit, 1);
00856            ++GS_pending;
00857            sprintf(buf,
00858                   "H mark /HWSize [%d %d] /ImagingBBox [0 0 %d %d] "
00859                   "currentdevice putdeviceprops pop\n"
00860                   "initgraphics [0 1 1 0 0 0] concat stop\n"
00861                   "%%%%xdvimark\n",
00862                   GS_page_w = globals.page.w,
00863                   GS_page_h = globals.page.h,
00864                   globals.page.h,
00865                   globals.page.w);
00866            gs_send(buf, strlen(buf));
00867            if (!in_header) {
00868               globals.ev.flags |= EV_NEWPAGE;    /* ||| redraw the page */
00869               longjmp(globals.ev.canit, 1);
00870            }
00871        }
00872 
00873        if (magnification != GS_mag) {
00874            if (globals.ev.flags & GS_ev_mask)
00875               longjmp(globals.ev.canit, 1);
00876            ++GS_pending;
00877            sprintf(buf,
00878                   "H TeXDict begin /DVImag %ld 1000 div def end stop\n"
00879                   "%%%%xdvimark\n",
00880                   GS_mag = magnification);
00881            gs_send(buf, strlen(buf));
00882        }
00883 
00884        if (mane.shrinkfactor != GS_shrink) {
00885            if (globals.ev.flags & GS_ev_mask)
00886               longjmp(globals.ev.canit, 1);
00887            ++GS_pending;
00888            sprintf(buf,
00889                   "H TeXDict begin %d %d div dup /Resolution X /VResolution X end stop\n"
00890                   "%%%%xdvimark\n",
00891                   resource.pixels_per_inch,
00892                   GS_shrink = mane.shrinkfactor);
00893            gs_send(buf, strlen(buf));
00894        }
00895     }
00896 }
00897 
00898 static void
00899 drawbegin_gs(int xul, int yul, const char *cp)
00900 {
00901     char buf[32];
00902     static const char str[] = " TeXDict begin\n";
00903     
00904     checkgs(False);
00905 
00906     if (!GS_active) {
00907        if (globals.ev.flags & GS_ev_mask)
00908            longjmp(globals.ev.canit, 1);
00909        ++GS_pending;
00910        gs_send(str, (sizeof str) - 1);
00911        GS_active = True;
00912     }
00913 
00914     /* This allows the X side to clear the page */
00915     XSync(DISP, False);
00916 
00917     sprintf(buf, "%d %d moveto\n", xul, yul);
00918     gs_send(buf, strlen(buf));
00919 /*     gs_send(clear_bg, strlen(clear_bg)); */
00920     if (globals.debug & DBG_PS)
00921        printf("drawbegin at %d,%d:  sending `%s'\n", xul, yul, cp);
00922 
00923     /*
00924      * added SU 2000/12/18:
00925      * Try to detect some simple PS rotating commands, and warn about them.
00926      * There are surely more than that ...
00927      */
00928     if (strstr(cp, "rotate") != NULL      /* used by graphics.sty */
00929        || strstr(cp, "RotBegin") != NULL  /* used by pstricks */
00930        ) {
00931        statusline_print(STATUS_SHORT,
00932                       "Warning: PS code on page %d may contain rotation, "
00933                       "which is not supported by xdvi", current_page + 1);
00934     }
00935 
00936     gs_send(cp, strlen(cp));
00937 }
00938 
00939 static void
00940 drawbegin_gs_box(int xul, int yul, const char *cp)
00941 {
00942     drawbegin_gs(xul, yul, cp);
00943 }
00944 
00945 
00946 void
00947 drawraw_gs(const char *cp)
00948 {
00949     if (!GS_active)
00950        return;
00951     if (globals.debug & DBG_PS)
00952        printf("raw ps sent to context: %s\n", cp);
00953     gs_send(cp, strlen(cp));
00954     gs_send("\n", 1);
00955 }
00956 
00957 static void
00958 drawfile_gs(const char *cp, FILE *f)
00959 {
00960     char canonical_path[MAXPATHLEN + 1];
00961     char *ret;
00962 
00963     fclose(f);       /* don't need it */
00964 
00965     if (!GS_active)
00966        return;
00967 
00968     if (globals.debug & DBG_PS)
00969        printf("original path: |%s|\n", cp);
00970 /*     drawraw_gs("newpath 0 0 moveto 0 1000 rlineto 1000 0 rlineto 0 -1000 rlineto closepath 1 setgray fill"); */
00971 #if 0
00972     /* if expand_symlinks or remove_dots from kpathsea were public,
00973        we could use the following: */
00974     ret = remove_dots(expand_symlinks(cp));
00975 #endif
00976 
00977     ret = REALPATH(cp, canonical_path);
00978     if (ret == NULL)
00979        XDVI_WARNING((stderr, "Couldn't canonicalize path \"%s\": %s. Sending to gs unchanged.",
00980                     cp, strerror(errno)));
00981     else
00982        cp = canonical_path;
00983 
00984     if (globals.debug & DBG_PS)
00985        printf("expanded path: |%s|\n", cp);
00986 
00987     gs_send("(", 1);
00988     gs_send(cp, strlen(cp));
00989     gs_send(")run\n", 5);
00990 /*     { */
00991 /*     char *clear_bg = "newpath 0 0 moveto 0 1000 rlineto 1000 0 rlineto 0 -1000 rlineto closepath .4 setgray fill\n"; */
00992 /*     gs_send(clear_bg, strlen(clear_bg)); */
00993 /*     } */
00994 }
00995 
00996 static void
00997 drawend_gs(const char *cp)
00998 {
00999     if (!GS_active)
01000        return;
01001     if (globals.debug & DBG_PS)
01002        printf("end ps: %s\n", cp);
01003     gs_send(cp, strlen(cp));
01004     gs_send("\n", 1);
01005     save_bbox();
01006 }
01007 
01008 static void
01009 beginheader_gs(void)
01010 {
01011     static const char str[] = "Hsave /xdvi$doc exch def\n";
01012 
01013     if (globals.debug & DBG_PS)
01014        puts("Running beginheader_gs()");
01015 
01016     checkgs(True);
01017 
01018     if (GS_active) {
01019        if (!GS_in_header)
01020            XDVI_FATAL((stderr, "Internal error in beginheader_gs()."));
01021        return;
01022     }
01023 
01024     if (globals.ev.flags & GS_ev_mask) {
01025 /*     fprintf(stderr, "longjmp!"); */
01026        longjmp(globals.ev.canit, 1);
01027     }
01028 
01029     GS_in_header = True;
01030     GS_ev_mask = GS_MASK_HEADER;
01031     ++GS_pending;
01032     if (GS_in_doc)
01033        gs_send("H", 1);
01034     else {
01035        gs_send(str, (sizeof str) - 1);
01036        GS_in_doc = True;
01037     }
01038     GS_active = True;
01039 }
01040 
01041 static void
01042 endheader_gs(void)
01043 {
01044     if (globals.debug & DBG_PS)
01045        puts("Running endheader_gs()");
01046 
01047     if (GS_active) {
01048        deactivate();
01049        waitack();
01050        GS_in_header = False;
01051        GS_ev_mask = GS_MASK_NORMAL;
01052     }
01053 }
01054 
01055 static void
01056 newdoc_gs(void)
01057 {
01058     static const char str[] = "Hxdvi$doc restore stop\n%%xdvimark\n";
01059 
01060     if (globals.debug & DBG_PS)
01061        puts("Running newdoc_gs()");
01062 
01063     if (GS_in_doc) {
01064        ++GS_pending;
01065        gs_send(str, (sizeof str) - 1);
01066        GS_mag = GS_shrink = -1;
01067        GS_in_doc = False;
01068 
01069        if (!GS_old) GS_page_w = GS_page_h = 0;
01070     }
01071 }
01072 
01073 void erasepage_gs(void)
01074 {
01075     /* In buffered mode, gs has no way of knowing that the screen has been 
01076        erased. Clear the gs window buffer with an erasepage command. */ 
01077     
01078     if (resource.gs_alpha && GS_pid > 0 && !GS_active) {
01079        if (globals.debug & DBG_PS)
01080            puts("erasing page!");
01081        beginheader_gs();
01082        drawraw_gs("erasepage");
01083        endheader_gs();
01084     }
01085 }
01086 
01087 #else
01088 /* silence `empty compilation unit' warnings */
01089 static void bar(void); static void foo() { bar(); } static void bar(void) { foo(); }
01090 #endif /* PS_GS */