home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / s / stex2-18.zip / SeeTeX / Xtex / DviPageGS-0.c < prev    next >
C/C++ Source or Header  |  1992-06-25  |  15KB  |  598 lines

  1. /*
  2.  * Copyright 1989 Dirk Grunwald
  3.  * 
  4.  * Permission to use, copy, modify, distribute, and sell this software
  5.  * and its documentation for any purpose is hereby granted without fee,
  6.  * provided that the above copyright notice appear in all copies and that
  7.  * both that copyright notice and this permission notice appear in
  8.  * supporting documentation, and that the name of Dirk Grunwald or M.I.T.
  9.  * not be used in advertising or publicity pertaining to distribution of
  10.  * the software without specific, written prior permission.  Dirk
  11.  * Grunwald and M.I.T. makes no representations about the suitability of
  12.  * this software for any purpose.  It is provided "as is" without express
  13.  * or implied warranty.
  14.  * 
  15.  * DIRK GRUNWALD AND M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
  16.  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17.  * FITNESS, IN NO EVENT SHALL M.I.T.  BE LIABLE FOR ANY SPECIAL, INDIRECT
  18.  * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  19.  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  20.  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  21.  * OR PERFORMANCE OF THIS SOFTWARE.
  22.  * 
  23.  * Author:
  24.  *     Dr. Dirk Grunwald
  25.  *     Dept. of Computer Science
  26.  *     Campus Box 430
  27.  *     Univ. of Colorado, Boulder
  28.  *     Boulder, CO 80309
  29.  * 
  30.  *     grunwald@colorado.edu
  31.  *     
  32.  * This file created by Shankar Ramamoorthy (shankar@cse.ucsc.edu) 04/92.
  33.  * Most of this comes from Tim Thiesen's Ghostview --- thanks Tim.
  34.  * 
  35.  */ 
  36.  
  37. #include <stdio.h>
  38. #include <sys/time.h>
  39. #include <X11/IntrinsicP.h>
  40. #include <X11/StringDefs.h>
  41. #include <X11/Xatom.h>
  42. #include <X11/Xmu/Atoms.h>
  43. #include <stdio.h>
  44. #include <assert.h>
  45. #include <varargs.h>
  46. #include <math.h>
  47. #include <ctype.h>
  48.  
  49. #include <signal.h>
  50. #ifdef SIGNALRETURNSINT
  51. #define SIGVAL int
  52. #else
  53. #define SIGVAL void
  54. #endif
  55.  
  56. #define NON_BLOCKING_IO
  57.  
  58. #ifdef NON_BLOCKING_IO
  59. #include <fcntl.h>
  60. /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
  61. #if !defined(O_NONBLOCK) && defined(O_NDELAY)
  62. #define O_NONBLOCK O_NDELAY
  63. #endif
  64. #endif
  65.  
  66. #include <errno.h>
  67. /* BSD 4.3 errno.h does not declare errno */
  68. extern int errno;
  69. /* Both error returns are checked for non-blocking I/O. */
  70. /* Manufacture the other error code if only one exists. */
  71. #if !defined(EWOULDBLOCK) && defined(EAGAIN)
  72. #define EWOULDBLOCK EAGAIN
  73. #endif
  74. #if !defined(EAGAIN) && defined(EWOULDBLOCK)
  75. #define EAGAIN EWOULDBLOCK
  76. #endif
  77.  
  78. #include "xtex.h"
  79. #include "dvi-simple.h"
  80. #include "DviPageP.h"
  81. #include "DviPageGS-0.h"
  82. #include "DviPageGS-1.h"
  83.  
  84.  
  85. /* GV_BUFSIZ is set to the minimum POSIX PIPE_MAX to ensure that
  86.  * nonblocking writes to ghostscript will work properly.
  87.  * GV_BUFSIZ must be 257 or greater to be able to read the largest
  88.  * PostScript input line allowed.  (257 <= GV_BUFSIZ <= 512)
  89.  */
  90. #define GV_BUFSIZ 512
  91.  
  92. /* length calculates string length at compile time */
  93. /* can only be used with character constants */
  94. #define length(a) (sizeof(a)-1)
  95. #define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
  96.  
  97. static Boolean broken_pipe = False;
  98.  
  99. #ifdef __STDC__
  100. static SIGVAL CathPipe (int);
  101. static int Output (XtPointer, int *, XtInputId *);
  102. static void InterpreterFailed (DviPageWidget);
  103. static Bool checkIfGhostMsg (Display *, XEvent *, char *);
  104. #else
  105. static SIGVAL CathPipe ();
  106. static int Output ();
  107. static void InterpreterFailed ();
  108. static Bool checkIfGhostMsg ();
  109. #endif
  110.  
  111. static SIGVAL
  112. CatchPipe(i)
  113.     int i;
  114. {
  115.     broken_pipe = True;
  116. #ifdef SIGNALRETURNSINT
  117.     return 0;
  118. #endif
  119. }
  120.  
  121.  
  122. /* 
  123.  *
  124.  * WriteBytes - Write bytes to ghostscript using non-blocking I/O.
  125.  * Also, pipe signals are caught during writing.  The return
  126.  * values are checked and the appropriate action is taken.  I do
  127.  * this at this low level, because it may not be appropriate for
  128.  * SIGPIPE to be caught for the overall application.
  129.  *
  130.  */
  131. void
  132. WriteBytes (w, ghostInputStr)
  133. DviPageWidget w;
  134. char *ghostInputStr;
  135.     {
  136. int bytes, bytesLeft;
  137. SIGVAL (*oldsig)();
  138.     
  139.     /* Sanity check */
  140.     if ((w->dviPage.ghostPID == -1) || (w->dviPage.ghostInputFD == -1))
  141.         /* Something wrong */
  142.         return;
  143.  
  144.     oldsig = signal(SIGPIPE, CatchPipe);
  145.     for (bytesLeft = strlen (ghostInputStr); bytesLeft; )
  146.         {
  147.         bytes = write (w->dviPage.ghostInputFD, ghostInputStr, bytesLeft);
  148.         signal(SIGPIPE, oldsig);
  149.     
  150.         if (broken_pipe) 
  151.             {
  152.             broken_pipe = False;
  153.             /* Interpreter failed */
  154.             InterpreterFailed (w);
  155.             return;
  156.             } 
  157.         else if (bytes == -1) 
  158.             {
  159.             if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) 
  160.                 {
  161.                 /* Something bad happened */
  162.                 InterpreterFailed (w);
  163.                 return;
  164.                 }
  165.             }
  166.         else 
  167.             {
  168.             bytesLeft -= bytes;
  169.             ghostInputStr += bytes;
  170.             }
  171.         }
  172.  
  173.     return;
  174.     }
  175.  
  176. /*
  177.  *
  178.  * Output - receive I/O from ghostscript's stdout and stderr.
  179.  * Pass this to the application via the output_callback. 
  180.  *
  181.  */
  182. static int
  183. Output(client_data, source, id)
  184. XtPointer client_data;
  185. int *source;
  186. XtInputId *id;
  187.     {
  188. DviPageWidget w = (DviPageWidget) client_data;
  189. char buf[GV_BUFSIZ+1];
  190. int bytes = 0;
  191.  
  192. #ifdef DEBUG
  193.     fprintf (stderr, "Output called.\n");
  194. #endif
  195.     
  196.     if (*source == w->dviPage.ghostOutputFD) 
  197.         {
  198.         bytes = read (w->dviPage.ghostOutputFD, buf, GV_BUFSIZ);
  199.         if (bytes == 0)
  200.             {
  201.             /* EOF occurred */
  202.             close (w->dviPage.ghostOutputFD);
  203.             w->dviPage.ghostOutputFD = -1;
  204.             XtRemoveInput (w->dviPage.ghostOutputId);
  205.             w->dviPage.ghostOutputId = None;
  206. #ifdef DEBUG
  207.             fprintf (stderr, "EOF on ghostscript stdout.\n");
  208.             fflush (stderr);
  209. #endif
  210.             return;
  211.             }
  212.         else if (bytes == -1) 
  213.             {
  214.             /* Something bad happened */
  215.             InterpreterFailed (w);
  216.             return;
  217.             }
  218.         }
  219.     else if (*source == w->dviPage.ghostErrorFD) 
  220.         {
  221.         bytes = read (w->dviPage.ghostErrorFD, buf, GV_BUFSIZ);
  222.         if (bytes == 0) 
  223.             {
  224.             /* EOF occurred */
  225.             close (w->dviPage.ghostErrorFD);
  226.             w->dviPage.ghostErrorFD = -1;
  227.             XtRemoveInput(w->dviPage.ghostErrorId);
  228.             w->dviPage.ghostErrorId = None;
  229. #ifdef DEBUG
  230.             fprintf (stderr, "EOF on ghostscript stderr.\n");
  231.             fflush (stderr);
  232. #endif
  233.             return;
  234.             }
  235.         else if (bytes == -1) 
  236.             {
  237.             /* Something bad happened */
  238.             InterpreterFailed (w);
  239.             return;
  240.             }
  241.         }
  242.  
  243.     if (bytes > 0) 
  244.         {
  245.         buf[bytes] = '\0';
  246. #ifdef DEBUG
  247.         fprintf (stderr, "Output :<%s>\n", buf);
  248.         fflush (stderr);
  249. #endif
  250.         /*
  251.         XtCallCallbackList((Widget) w, w->dviPage.ghostOutput, (XtPointer) buf);
  252.         */
  253.         DialogMessage (buf, False);
  254.         }
  255.     }
  256.  
  257. /* This routine starts the interpreter.  It sets the DISPLAY and 
  258.  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
  259.  * contains the Window that ghostscript should write on.
  260.  *
  261.  * This routine also opens pipes for stdout and stderr and initializes
  262.  * application input events for them.  If input to ghostscript is not
  263.  * from a file, a pipe for stdin is created.  This pipe is setup for
  264.  * non-blocking I/O so that the user interface never "hangs" because of
  265.  * a write to ghostscript.
  266.  */
  267. void
  268. StartInterpreter (w)
  269. DviPageWidget w;
  270.     {
  271. DviPageWidgetClass wc = (DviPageWidgetClass) XtClass(w);
  272. int std_in[2];
  273. int std_out[2];
  274. int std_err[2];
  275. char buf[GV_BUFSIZ];
  276. static char gsEnv[GV_BUFSIZ], dispEnv[GV_BUFSIZ], layEnv[GV_BUFSIZ];
  277. char *gsSearchPath;
  278. #define NUM_ARGS 100
  279. char *argv[NUM_ARGS];
  280. int argc = 0;
  281. int ret;
  282. XWindowAttributes attrs;
  283. float trueDpi;
  284. XEvent event;
  285.  
  286.     if (w->dviPage.ghostPID != -1)
  287.         StopInterpreter (w);
  288.  
  289.     if (w->dviPage.ghostPixmap == -1)
  290.         /* Unable to get pixmap; dont run interpreter. */
  291.         return;
  292.     
  293.     /* Prepare a <DONE> event in case exec fails */
  294.     event.xclient.type = ClientMessage;
  295.     event.xclient.display = XtDisplay(w);
  296.     event.xclient.window = XtWindow (w);
  297.     event.xclient.message_type = XmuInternAtom(XtDisplay(w),
  298.         wc->dviPage_class.done);
  299.     event.xclient.format = 32;
  300.     event.xclient.data.l[0] = None;
  301.     event.xclient.data.l[1] = w->dviPage.ghostPixmap;
  302.  
  303.     argv[argc++] = w->dviPage.interpreter;
  304.     argv[argc++] = "-dQUIET";
  305.     argv[argc++] = "-dNOPAUSE";
  306.     gsSearchPath = XtMalloc ((strlen (w->dviPage.ghostSearchPath) + 3) *
  307.         sizeof (char));
  308.     sprintf (gsSearchPath, "-I%s", w->dviPage.ghostSearchPath);
  309.     argv[argc++] = gsSearchPath;
  310.     argv[argc++] = "-";
  311.     argv[argc++] = NULL;
  312.     
  313.     ret = pipe(std_in);
  314.     if (ret == -1) 
  315.         {
  316.         perror("Could not create pipe");
  317.         return;
  318.         }
  319.     
  320.     ret = pipe(std_out);
  321.     if (ret == -1) 
  322.         {
  323.         close (std_in[0]);
  324.         close (std_in[1]);
  325.         perror("Could not create pipe");
  326.         return;
  327.         }
  328.     ret = pipe(std_err);
  329.     if (ret == -1) 
  330.         {
  331.         close (std_in[0]);
  332.         close (std_in[1]);
  333.         close (std_out[0]);
  334.         close (std_out[1]);
  335.         perror("Could not create pipe");
  336.         return;
  337.         }
  338.  
  339.     /* 
  340.      * 72.0 seems to be the one that works --- don't mess with it.
  341.      * See also DviPage2.c
  342.      */
  343.     trueDpi = (w->dviPage.trueDpi == 0.0) ? 72.0 : w->dviPage.trueDpi;
  344.  
  345.     /* Set up environment and property for ghostscript */
  346.     sprintf (buf, "0 0 0 0 %d %d %f %f 0 0 0 0 %d %d",
  347.         w->dviPage.pixelsWide, w->dviPage.pixelsHigh,
  348.         trueDpi, trueDpi,
  349.         w->dviPage.pixelsWide, w->dviPage.pixelsHigh);
  350.     XChangeProperty(XtDisplay (w), XtWindow (w), 
  351.         XmuInternAtom (XtDisplay(w), wc->dviPage_class.ghostview), 
  352.         XA_STRING, 8, PropModeReplace, (unsigned char *) buf, 
  353.         strlen (buf));
  354.  
  355.     sprintf (buf, "[ exec ] %s\n", argv[0]);
  356.     DialogMessage (buf, False);
  357.  
  358.     w->dviPage.ghostPID = fork();
  359.     
  360.     if (w->dviPage.ghostPID == 0) 
  361.         {
  362.         /* child */
  363.         sprintf (gsEnv, "GHOSTVIEW=%d %d", XtWindow (w), w->dviPage.ghostPixmap);
  364.         putenv (gsEnv);
  365.         sprintf( dispEnv,"DISPLAY=%s",XDisplayString(XtDisplay(w)));
  366.         putenv (dispEnv);
  367.  
  368.         close (std_out[0]);
  369.         close (std_err[0]);
  370.         dup2 (std_out[1], 1);
  371.         close (std_out[1]);
  372.         dup2 (std_err[1], 2);
  373.         close (std_err[1]);
  374.  
  375.         close(std_in[1]);
  376.         dup2(std_in[0], 0);
  377.         close(std_in[0]);
  378.  
  379.         execvp (argv[0], argv);
  380.         /* Failed */
  381.         perror (argv[0]);
  382.         XSendEvent(XtDisplay(w), XtWindow (w), False, NoEventMask, &event);
  383.         XFlush (XtDisplay (w));
  384.         exit (-1);
  385.         }
  386.     else
  387.         {
  388.         {
  389. #ifdef NON_BLOCKING_IO
  390.         int result;
  391. #endif
  392.         close(std_in[0]);
  393.         
  394. #ifdef NON_BLOCKING_IO
  395.         result = fcntl(std_in[1], F_GETFL, 0);
  396.         result = result | O_NONBLOCK;
  397.         result = fcntl(std_in[1], F_SETFL, result);
  398. #endif
  399.  
  400.         w->dviPage.ghostInputFD = std_in[1];
  401.         w->dviPage.ghostInputId = None;
  402.         }
  403.  
  404.         close (std_out[1]);
  405.         w->dviPage.ghostOutputFD = std_out[0];
  406.         w->dviPage.ghostOutputId = 
  407.             XtAppAddInput(XtWidgetToApplicationContext ((Widget) w), std_out[0],
  408.             (XtPointer) XtInputReadMask, 
  409.             (XtInputCallbackProc) Output, (XtPointer) w);
  410.  
  411.         close(std_err[1]);
  412.         w->dviPage.ghostErrorFD = std_err[0];
  413.         w->dviPage.ghostErrorId = 
  414.             XtAppAddInput(XtWidgetToApplicationContext ((Widget) w), std_err[0],
  415.             (XtPointer) XtInputReadMask, 
  416.             (XtInputCallbackProc) Output, (XtPointer) w);
  417.  
  418.         WriteBytes (w, w->dviPage.gsPreamble);
  419.         WriteBytes (w, "\n");
  420.  
  421.         /* Send a spurious showpage --- this causes a clear to white
  422.          */
  423.         WriteBytes (w, "@xtexGhostShowpage\n");
  424.  
  425.         /* Wait for acknowledgement from ghostscript */
  426.         waitCopyAndAcknowledge (w, False, 0, 0, 0, 0, 0, 0);
  427.  
  428.         XtFree (dispEnv);
  429.         XtFree (gsSearchPath);
  430.         }
  431.     }
  432.  
  433. void
  434. StopInterpreter (w)
  435. DviPageWidget w;
  436.     {    
  437.  
  438.     if (w->dviPage.ghostPID >= 0)
  439.         {
  440.         kill (w->dviPage.ghostPID, SIGTERM);
  441.         wait (0);
  442.         w->dviPage.ghostPID = -1;
  443.         }
  444.  
  445.     if (w->dviPage.ghostInputFD >= 0)
  446.         {
  447.         close (w->dviPage.ghostInputFD);
  448.         if (w->dviPage.ghostInputId != None)
  449.             XtRemoveInput (w->dviPage.ghostInputId);
  450.         }
  451.  
  452.     if (w->dviPage.ghostOutputFD >= 0)
  453.         {
  454.         /*
  455.          * Make sure there's nothing.
  456.          * We need to do this because we are not doing neat event driven
  457.          * programming --- we wait for events from gs, and if we get a done
  458.          * event, we kill the interp and remove input notification.
  459.          */
  460.         Output ((XtPointer) w, &w->dviPage.ghostOutputFD,
  461.             &w->dviPage.ghostOutputId);
  462.         close (w->dviPage.ghostOutputFD);
  463.         if (w->dviPage.ghostOutputId != None)
  464.             XtRemoveInput (w->dviPage.ghostOutputId);
  465.         }
  466.  
  467.     if (w->dviPage.ghostErrorFD >= 0)
  468.         {
  469.         /*
  470.          * Make sure there's nothing.
  471.          * We need to do this because we are not doing neat event driven
  472.          * programming --- we wait for events from gs, and if we get a done
  473.          * event, we kill the interp and remove input notification.
  474.          */
  475.         Output ((XtPointer) w, &w->dviPage.ghostErrorFD,
  476.             &w->dviPage.ghostErrorId);
  477.         close (w->dviPage.ghostErrorFD);
  478.         if (w->dviPage.ghostErrorId != None)
  479.             XtRemoveInput (w->dviPage.ghostErrorId);
  480.         }
  481.     }
  482.  
  483. static void
  484. InterpreterFailed (w)
  485. DviPageWidget w;
  486.     {
  487.     /*
  488.     DialogMessage ("***ERROR*** Ghostscript ended unexpectedly.\n", False);
  489.     StopInterpreter(w);
  490.     */
  491.     }
  492.  
  493. static Bool
  494. checkIfGhostMsg (display, event, arg)
  495. Display *display;
  496. XEvent *event;
  497. char *arg;
  498.     {
  499. DviPageWidget w = (DviPageWidget) arg;
  500. DviPageWidgetClass wc = (DviPageWidgetClass) XtClass (w);
  501.  
  502.     if (event->type == ClientMessage)
  503.         {
  504.         /*
  505.          * Right type of event.
  506.          * Make sure this is from ghostscript
  507.          *
  508.          */
  509.         if ((event->xclient.message_type == 
  510.                     XmuInternAtom(display, wc->dviPage_class.page)) || 
  511.                 (event->xclient.message_type ==
  512.                     XmuInternAtom(display, wc->dviPage_class.done)))
  513.             {
  514.             /*
  515.              * Right type of message.
  516.              * Make sure its for this page.
  517.              *
  518.              */
  519.             return (w->dviPage.ghostPixmap == event->xclient.data.l[1]);
  520.             }
  521.         else
  522.             return (False);
  523.         }
  524.     else
  525.         return (False);
  526.     }
  527.  
  528. void
  529. waitCopyAndAcknowledge (w, copyFlag, ox, oy, width, height, rx, ry)
  530. DviPageWidget w;
  531. Bool copyFlag;
  532. int ox, oy, width, height, rx, ry;
  533.     {
  534. DviPageWidgetClass wc = (DviPageWidgetClass) XtClass (w);
  535. XEvent event;
  536.  
  537.     /* Make sure interpreter got started */
  538.     if (w->dviPage.ghostPID == -1)
  539.         return;
  540.  
  541.     /* Wait for message from ghostscript */
  542.     XPeekIfEvent (XtDisplay (w), &event, checkIfGhostMsg, (XtPointer) w);
  543.     /* Get the event, deleting it from the event queue */
  544.     XIfEvent (XtDisplay (w), &event, checkIfGhostMsg, (XtPointer) w);
  545.  
  546.     /* Process event */
  547.     if (event.xclient.message_type ==
  548.             XmuInternAtom(XtDisplay(w), wc->dviPage_class.page))
  549.         {
  550.         if (copyFlag == True)
  551.             {
  552.             /* Copy rectangle from pixmap to window */
  553.             XCopyArea (XtDisplay (w), w->dviPage.ghostPixmap, XtWindow (w),
  554.                 w->dviPage.paintGC, ox, oy, width, height, rx, ry);
  555.             /*
  556.             XDrawRectangle (XtDisplay (w), XtWindow (w),
  557.                 w->dviPage.paintGC, rx, ry, width, height);
  558.             */
  559.             }
  560.     
  561.         /* Send a NEXT event to ghostscript */
  562.         w->dviPage.ghostMsgWin = event.xclient.data.l[0];
  563.         event.xclient.type = ClientMessage;
  564.         event.xclient.display = XtDisplay(w);
  565.         event.xclient.window = w->dviPage.ghostMsgWin;
  566.         event.xclient.message_type = XmuInternAtom(XtDisplay(w),
  567.             wc->dviPage_class.next);
  568.         event.xclient.format = 32;
  569.         XSendEvent(XtDisplay(w), w->dviPage.ghostMsgWin, False, 0, &event);
  570.         /*
  571.         XFlush (XtDisplay (w));
  572.         */
  573.         }
  574.     else if (event.xclient.message_type ==
  575.                     XmuInternAtom(XtDisplay(w), wc->dviPage_class.done))
  576.         {
  577.         /*
  578.         DialogMessage ("***ERROR*** Ghostscript ended unexpectedly.\n", False);
  579.         */
  580.         /* Failed; just draw a rectangle. */
  581.         if (copyFlag == True)
  582.             XCopyArea (XtDisplay (w), w->dviPage.ghostPixmap, XtWindow (w),
  583.                 w->dviPage.paintGC, ox, oy, width, height, rx, ry);
  584.             /*
  585.             XDrawRectangle (XtDisplay (w), XtWindow (w), w->dviPage.paintGC, 
  586.                 rx, ry, width, height);
  587.             */
  588.         StopInterpreter(w);
  589.         DialogMessage ("[ gs ] done\n", False);
  590.         }
  591.     else
  592.         {
  593.         DialogMessage ("***ERROR*** Unknown message type from Ghostscript\n",
  594.             False);
  595.         StopInterpreter(w);
  596.         }
  597.     }
  598.