"Fossies" - the Fresh Open Source Software Archive

Member "ghostview-1.4.1/Ghostview.c" (27 Oct 1992, 59345 Bytes) of package /linux/misc/old/ghost/gnu/ghostview/ghostview-1.4.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * Ghostview.c -- Ghostview widget.
    3  * Copyright (C) 1992  Timothy O. Theisen
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; either version 2 of the License, or
    8  * (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   18  *
   19  *   Author: Tim Theisen           Systems Programmer
   20  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
   21  *     UUCP: uwvax!tim             University of Wisconsin-Madison
   22  *    Phone: (608)262-0438         1210 West Dayton Street
   23  *      FAX: (608)262-9777         Madison, WI   53706
   24  */
   25 
   26 #include <X11/IntrinsicP.h>
   27 #include <X11/StringDefs.h>
   28 #include <X11/Xatom.h>
   29 #include <X11/Xproto.h>
   30 #include <X11/Xos.h>
   31 #include "GhostviewP.h"
   32 #include <ctype.h>
   33 
   34 #ifndef XlibSpecificationRelease
   35 typedef char *XPointer;
   36 #endif
   37 
   38 #include <signal.h>
   39 #ifdef SIGNALRETURNSINT
   40 #define SIGVAL int
   41 #else
   42 #define SIGVAL void
   43 #endif
   44 
   45 #ifdef NON_BLOCKING_IO
   46 #include <fcntl.h>
   47 /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
   48 #if !defined(O_NONBLOCK) && defined(O_NDELAY)
   49 #define O_NONBLOCK O_NDELAY
   50 #endif
   51 #endif
   52 
   53 #include <errno.h>
   54 /* BSD 4.3 errno.h does not declare errno */
   55 extern int errno;
   56 /* Both error returns are checked for non-blocking I/O. */
   57 /* Manufacture the other error code if only one exists. */
   58 #if !defined(EWOULDBLOCK) && defined(EAGAIN)
   59 #define EWOULDBLOCK EAGAIN
   60 #endif
   61 #if !defined(EAGAIN) && defined(EWOULDBLOCK)
   62 #define EAGAIN EWOULDBLOCK
   63 #endif
   64 
   65 #ifndef VMS
   66 /* GV_BUFSIZ is set to the minimum POSIX PIPE_BUF to ensure that
   67  * nonblocking writes to ghostscript will work properly.
   68  */
   69 #define GV_BUFSIZ 512
   70 #else /* VMS */
   71 /*
   72 ** GV_BUFSIZ is the maximum length line we can handle, so we up it to 1024
   73 */
   74 #define GV_BUFSIZ 1024
   75 #endif /* VMS */
   76 
   77 /* length calculates string length at compile time */
   78 /* can only be used with character constants */
   79 #define length(a) (sizeof(a)-1)
   80 #define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
   81 
   82 static void ComputeXdpi();
   83 static void ComputeYdpi();
   84 
   85 static XtResource resources[] = {
   86 #define offset(field) XtOffsetOf(GhostviewRec, ghostview.field)
   87     { XtNbottomMargin, XtCMargin, XtRInt, sizeof(int),
   88       offset(bottom_margin), XtRImmediate, (XtPointer)0 },
   89     { XtNbusyCursor, XtCCursor, XtRCursor, sizeof(XtPointer),
   90       offset(busy_cursor), XtRString, "watch" },
   91     { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
   92       offset(callback), XtRCallback, (XtPointer)NULL },
   93     { XtNcursor, XtCCursor, XtRCursor, sizeof(XtPointer),
   94       offset(cursor), XtRString, "crosshair" },
   95     { XtNfilename, XtCFilename, XtRString, sizeof(String),
   96       offset(filename), XtRString, (XtPointer)NULL },
   97     { XtNinterpreter, XtCInterpreter, XtRString, sizeof(String),
   98       offset(interpreter), XtRString, "gs" },
   99     { XtNleftMargin, XtCMargin, XtRInt, sizeof(int),
  100       offset(left_margin), XtRImmediate, (XtPointer)0 },
  101     { XtNllx, XtCBoundingBox, XtRInt, sizeof(int),
  102       offset(llx), XtRImmediate, (XtPointer)0 },
  103     { XtNlly, XtCBoundingBox, XtRInt, sizeof(int),
  104       offset(lly), XtRImmediate, (XtPointer)0 },
  105     { XtNmessageCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  106       offset(message_callback), XtRCallback, (XtPointer)NULL },
  107     { XtNorientation, XtCOrientation, XtRPageOrientation,
  108       sizeof(XtPageOrientation), offset(orientation), XtRImmediate,
  109       (XtPointer)XtPageOrientationPortrait },
  110     { XtNoutputCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  111       offset(output_callback), XtRCallback, (XtPointer)NULL },
  112     { XtNpreload, XtCPreload, XtRString, sizeof(String),
  113       offset(preload), XtRString, (XtPointer)NULL },
  114     { XtNquiet, XtCQuiet, XtRBoolean, sizeof(Boolean),
  115       offset(quiet), XtRImmediate, (XtPointer)True },
  116     { XtNrightMargin, XtCMargin, XtRInt, sizeof(int),
  117       offset(right_margin), XtRImmediate, (XtPointer)0 },
  118     { XtNtopMargin, XtCMargin, XtRInt, sizeof(int),
  119       offset(top_margin), XtRImmediate, (XtPointer)0 },
  120     { XtNuseBackingPixmap, XtCUseBackingPixmap, XtRBoolean, sizeof(Boolean),
  121       offset(use_bpixmap), XtRImmediate, (XtPointer)True },
  122     { XtNurx, XtCBoundingBox, XtRInt, sizeof(int),
  123       offset(urx), XtRImmediate, (XtPointer)612 },
  124     { XtNury, XtCBoundingBox, XtRInt, sizeof(int),
  125       offset(ury), XtRImmediate, (XtPointer)792 },
  126     { XtNxdpi, XtCResolution, XtRFloat, sizeof(float),
  127       offset(xdpi), XtRCallProc, (XtPointer)ComputeXdpi },
  128     { XtNydpi, XtCResolution, XtRFloat, sizeof(float),
  129       offset(ydpi), XtRCallProc, (XtPointer)ComputeYdpi },
  130 #undef offset
  131 };
  132 
  133 static void Message();
  134 static void Notify();
  135 static void Input();
  136 static void Output();
  137 
  138 static void ClassInitialize();
  139 static void ClassPartInitialize();
  140 static void Initialize();
  141 static void Realize();
  142 static void Destroy();
  143 static void Resize();
  144 static Boolean SetValues();
  145 static XtGeometryResult QueryGeometry();
  146 
  147 static void Layout();
  148 static Boolean ComputeSize();
  149 static void ChangeSize();
  150 static Boolean Setup();
  151 static void StartInterpreter();
  152 static void StopInterpreter();
  153 static void InterpreterFailed();
  154 
  155 static XtActionsRec actions[] =
  156 {
  157     {"message", Message},
  158     {"notify",  Notify},
  159 };
  160 
  161 /* notify takes zero to four parameters.  The first two give the width and
  162  * height of the zoom requested in the default user coordinate system.
  163  * If they are omitted, a default value of 72 is provided.  If the second
  164  * parameter is omitted, the zoom area is assumed to be a square.
  165  * The next two parameters give the desired resolution of the zoom window.
  166  * If they are omitted, a default value of 300 is provided. If the four
  167  * parameter is omitted, the y resolution is assumed to be equal to the
  168  * x resolution.
  169  */
  170 static char translations[] =
  171 "<Message>:     message()   \n\
  172 <Btn1Down>:     notify(72)  \n\
  173 <Btn2Down>:     notify(108) \n\
  174 <Btn3Down>:     notify(144) \n\
  175 ";
  176 
  177 GhostviewClassRec ghostviewClassRec = {
  178   { /* core fields */
  179     /* superclass       */  (WidgetClass) &coreClassRec,
  180     /* class_name       */  "Ghostview",
  181     /* widget_size      */  sizeof(GhostviewRec),
  182     /* class_initialize     */  ClassInitialize,
  183     /* class_part_initialize    */  ClassPartInitialize,
  184     /* class_inited     */  FALSE,
  185     /* initialize       */  Initialize,
  186     /* initialize_hook      */  NULL,
  187     /* realize          */  Realize,
  188     /* actions          */  actions,
  189     /* num_actions      */  XtNumber(actions),
  190     /* resources        */  resources,
  191     /* num_resources        */  XtNumber(resources),
  192     /* xrm_class        */  NULLQUARK,
  193     /* compress_motion      */  TRUE,
  194     /* compress_exposure    */  TRUE,
  195     /* compress_enterleave  */  TRUE,
  196     /* visible_interest     */  FALSE,
  197     /* destroy          */  Destroy,
  198     /* resize           */  Resize,
  199     /* expose           */  NULL,
  200     /* set_values       */  SetValues,
  201     /* set_values_hook      */  NULL,
  202     /* set_values_almost    */  XtInheritSetValuesAlmost,
  203     /* get_values_hook      */  NULL,
  204     /* accept_focus     */  NULL,
  205     /* version          */  XtVersion,
  206     /* callback_private     */  NULL,
  207     /* tm_table         */  translations,
  208     /* query_geometry       */  QueryGeometry,
  209     /* display_accelerator  */  XtInheritDisplayAccelerator,
  210     /* extension        */  NULL
  211   },
  212   { /* ghostview fields */
  213     /* ghostview        */  NULL,
  214     /* next         */  NULL,
  215     /* page         */  NULL,
  216     /* done         */  NULL
  217   }
  218 };
  219 
  220 WidgetClass ghostviewWidgetClass = (WidgetClass)&ghostviewClassRec;
  221 
  222 /* Procedures that compute the default xdpi and ydpi from display parameters */
  223 
  224 static void
  225 ComputeXdpi(w, offset, value)
  226     Widget w;
  227     int offset;
  228     XrmValue *value;
  229 {
  230     static float xdpi;
  231     xdpi = 25.4 * WidthOfScreen(XtScreen(w)) / WidthMMOfScreen(XtScreen(w));
  232     value->addr = (XtPointer) &xdpi;
  233 }
  234 
  235 static void
  236 ComputeYdpi(w, offset, value)
  237     Widget w;
  238     int offset;
  239     XrmValue *value;
  240 {
  241     static float ydpi;
  242     ydpi = 25.4 * HeightOfScreen(XtScreen(w)) / HeightMMOfScreen(XtScreen(w));
  243     value->addr = (XtPointer) &ydpi;
  244 }
  245 
  246 /* Message action routine.
  247  * Passes ghostscript message events back to application via
  248  * the message callback.  It also marks the interpreter as
  249  * being not busy at the end of page, and stops the interpreter
  250  * when it send a "done" message.
  251  */
  252 static void
  253 Message(w, event, params, num_params)
  254     Widget w;
  255     XEvent *event;
  256     String *params;     /* unused */
  257     Cardinal *num_params;   /* unused */
  258 {
  259     GhostviewWidget gvw = (GhostviewWidget) w;
  260     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  261 
  262     gvw->ghostview.mwin = event->xclient.data.l[0];
  263     if (event->xclient.message_type ==
  264     XmuInternAtom(XtDisplay(w), gvc->ghostview_class.page)) {
  265     gvw->ghostview.busy = False;
  266     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  267     XtCallCallbackList(w, gvw->ghostview.message_callback, "Page");
  268     } else if (event->xclient.message_type ==
  269            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.done)) {
  270     StopInterpreter(w);
  271     XtCallCallbackList(w, gvw->ghostview.message_callback, "Done");
  272     }
  273 }
  274 
  275 /* Notify action routine.
  276  * Calculates where the user clicked in the default user coordinate system.
  277  * Call the callbacks with the point of click and size of zoom window
  278  * requested.
  279  */
  280 static void
  281 Notify(w, event, params, num_params)
  282     Widget w;
  283     XEvent *event;
  284     String *params;
  285     Cardinal *num_params;
  286 {
  287     GhostviewWidget gvw = (GhostviewWidget) w;
  288     GhostviewReturnStruct ret_val;
  289 
  290     /* notify takes zero to four parameters.  The first two give the width and
  291      * height of the zoom requested in the default user coordinate system.
  292      * If they are omitted, a default value of 72 is provided.  If the second
  293      * parameter is omitted, the zoom area is assumed to be a square.
  294      * The next two parameters give the desired resolution of the zoom window.
  295      * If they are omitted, a default value of 300 is provided. If the four
  296      * parameter is omitted, the y resolution is assumed to be equal to the
  297      * x resolution.
  298      */
  299     switch (*num_params) {
  300     case 0:
  301     ret_val.width = ret_val.height = 72;
  302     ret_val.xdpi = ret_val.ydpi = 300;
  303     break;
  304     case 1:
  305     ret_val.width = ret_val.height = atoi(params[0]);
  306     ret_val.xdpi = ret_val.ydpi = 300;
  307     break;
  308     case 2:
  309     ret_val.width = atoi(params[0]);
  310     ret_val.height = atoi(params[1]);
  311     ret_val.xdpi = ret_val.ydpi = 300;
  312     break;
  313     case 3:
  314     ret_val.width = atoi(params[0]);
  315     ret_val.height = atoi(params[1]);
  316     ret_val.xdpi = ret_val.ydpi = atoi(params[2]);
  317     break;
  318     default:
  319     ret_val.width = atoi(params[0]);
  320     ret_val.height = atoi(params[1]);
  321     ret_val.xdpi = atoi(params[2]);
  322     ret_val.ydpi = atoi(params[3]);
  323     break;
  324     }
  325 
  326     switch (gvw->ghostview.orientation) {
  327     case XtPageOrientationPortrait:
  328     ret_val.psx = gvw->ghostview.llx + 
  329               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  330     ret_val.psy = gvw->ghostview.ury - 
  331               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  332     break;
  333     case XtPageOrientationLandscape:
  334     ret_val.psx = gvw->ghostview.llx + 
  335               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  336     ret_val.psy = gvw->ghostview.lly + 
  337               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  338     break;
  339     case XtPageOrientationUpsideDown:
  340     ret_val.psx = gvw->ghostview.urx - 
  341               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  342     ret_val.psy = gvw->ghostview.lly + 
  343               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  344     break;
  345     case XtPageOrientationSeascape:
  346     ret_val.psx = gvw->ghostview.urx - 
  347               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  348     ret_val.psy = gvw->ghostview.ury - 
  349               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  350     break;
  351     }
  352     XtCallCallbackList(w, gvw->ghostview.callback, (XtPointer) &ret_val);
  353 }
  354 
  355 #ifndef SEEK_SET
  356 #define SEEK_SET 0
  357 #endif
  358 
  359 static Boolean broken_pipe = False;
  360 
  361 static SIGVAL
  362 CatchPipe(i)
  363     int i;
  364 {
  365     broken_pipe = True;
  366 #ifdef SIGNALRETURNSINT
  367     return 0;
  368 #endif
  369 }
  370 
  371 #ifndef VMS
  372 
  373 /* Input - Feed data to ghostscript's stdin.
  374  * Write bytes to ghostscript using non-blocking I/O.
  375  * Also, pipe signals are caught during writing.  The return
  376  * values are checked and the appropriate action is taken.  I do
  377  * this at this low level, because it may not be appropriate for
  378  * SIGPIPE to be caught for the overall application.
  379  */
  380 
  381 static void
  382 Input(client_data, source, id)
  383     XtPointer client_data;
  384     int *source;
  385     XtInputId *id;
  386 {
  387     Widget w = (Widget) client_data;
  388     GhostviewWidget gvw = (GhostviewWidget) w;
  389     int bytes_written;
  390     SIGVAL (*oldsig)();
  391 
  392     oldsig = signal(SIGPIPE, CatchPipe);
  393 
  394 #ifdef NON_BLOCKING_IO
  395     do {
  396 #endif
  397 
  398     if (gvw->ghostview.buffer_bytes_left == 0) {
  399 
  400         /* Get a new section if required */
  401         if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
  402         struct record_list *ps_old = gvw->ghostview.ps_input;
  403         gvw->ghostview.ps_input = ps_old->next;
  404         if (ps_old->close) fclose(ps_old->fp);
  405         XtFree((char *)ps_old);
  406         }
  407 
  408         /* Have to seek at the beginning of each section */
  409         if (gvw->ghostview.ps_input &&
  410         gvw->ghostview.ps_input->seek_needed) {
  411         if (gvw->ghostview.ps_input->len > 0)
  412             fseek(gvw->ghostview.ps_input->fp,
  413               gvw->ghostview.ps_input->begin, SEEK_SET);
  414         gvw->ghostview.ps_input->seek_needed = False;
  415         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
  416         }
  417 
  418         if (gvw->ghostview.bytes_left > GV_BUFSIZ) {
  419         gvw->ghostview.buffer_bytes_left =
  420             fread(gvw->ghostview.input_buffer,
  421                   sizeof (char), GV_BUFSIZ,
  422                   gvw->ghostview.ps_input->fp);
  423         } else if (gvw->ghostview.bytes_left > 0) {
  424         gvw->ghostview.buffer_bytes_left =
  425             fread(gvw->ghostview.input_buffer,
  426                   sizeof (char), gvw->ghostview.bytes_left,
  427                   gvw->ghostview.ps_input->fp);
  428         } else {
  429         gvw->ghostview.buffer_bytes_left = 0;
  430         }
  431         if (gvw->ghostview.bytes_left > 0 &&
  432         gvw->ghostview.buffer_bytes_left == 0) {
  433         InterpreterFailed(w);   /* Error occurred */
  434         }
  435         gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
  436         gvw->ghostview.bytes_left -= gvw->ghostview.buffer_bytes_left;
  437     }
  438 
  439     if (gvw->ghostview.buffer_bytes_left > 0) {
  440         bytes_written = write(gvw->ghostview.interpreter_input,
  441                   gvw->ghostview.input_buffer_ptr,
  442                   gvw->ghostview.buffer_bytes_left);
  443 
  444         if (broken_pipe) {
  445         broken_pipe = False;
  446         InterpreterFailed(w);       /* Something bad happened */
  447         } else if (bytes_written == -1) {
  448         if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
  449             InterpreterFailed(w);   /* Something bad happened */
  450         }
  451         } else {
  452         gvw->ghostview.buffer_bytes_left -= bytes_written;
  453         gvw->ghostview.input_buffer_ptr += bytes_written;
  454         }
  455     }
  456 #ifdef NON_BLOCKING_IO
  457     } while(gvw->ghostview.ps_input &&
  458         gvw->ghostview.buffer_bytes_left == 0);
  459 #endif
  460     signal(SIGPIPE, oldsig);
  461     if (gvw->ghostview.ps_input == NULL &&
  462     gvw->ghostview.buffer_bytes_left == 0) {
  463     if (gvw->ghostview.interpreter_input_id != None) {
  464         XtRemoveInput(gvw->ghostview.interpreter_input_id);
  465         gvw->ghostview.interpreter_input_id = None;
  466     }
  467     }
  468 }
  469 
  470 /* Output - receive I/O from ghostscript's stdout and stderr.
  471  * Pass this to the application via the output_callback. */
  472 static void
  473 Output(client_data, source, id)
  474     XtPointer client_data;
  475     int *source;
  476     XtInputId *id;
  477 {
  478     Widget w = (Widget) client_data;
  479     GhostviewWidget gvw = (GhostviewWidget) w;
  480     char buf[GV_BUFSIZ+1];
  481     int bytes = 0;
  482 
  483     if (*source == gvw->ghostview.interpreter_output) {
  484     bytes = read(gvw->ghostview.interpreter_output, buf, GV_BUFSIZ);
  485     if (bytes == 0) { /* EOF occurred */
  486         close(gvw->ghostview.interpreter_output);
  487         gvw->ghostview.interpreter_output = -1;
  488         XtRemoveInput(gvw->ghostview.interpreter_output_id);
  489         return;
  490     } else if (bytes == -1) {
  491         InterpreterFailed(w);       /* Something bad happened */
  492         return;
  493     }
  494     } else if (*source == gvw->ghostview.interpreter_error) {
  495     bytes = read(gvw->ghostview.interpreter_error, buf, GV_BUFSIZ);
  496     if (bytes == 0) { /* EOF occurred */
  497         close(gvw->ghostview.interpreter_error);
  498         gvw->ghostview.interpreter_error = -1;
  499         XtRemoveInput(gvw->ghostview.interpreter_error_id);
  500         return;
  501     } else if (bytes == -1) {
  502         InterpreterFailed(w);       /* Something bad happened */
  503         return;
  504     }
  505     }
  506     if (bytes > 0) {
  507     buf[bytes] = '\0';
  508     XtCallCallbackList(w, gvw->ghostview.output_callback, (XtPointer) buf);
  509     }
  510 }
  511 
  512 #endif /* VMS */
  513 
  514 /* Register the type converter required for the PageOrientation. */
  515 /* This routine is called exactly once. */
  516 static void
  517 ClassInitialize()
  518 {
  519     XtSetTypeConverter(XtRString, XtRPageOrientation,
  520                XmuCvtStringToPageOrientation, NULL, 0,
  521                XtCacheAll, NULL);
  522 }
  523 
  524 /* Get atoms needed to communicate with ghostscript. */
  525 /* This routine is called once per display. */
  526 static void
  527 ClassPartInitialize(class)
  528     WidgetClass class;
  529 {
  530     GhostviewWidgetClass gvc = (GhostviewWidgetClass)class;
  531     gvc->ghostview_class.ghostview = XmuMakeAtom("GHOSTVIEW");
  532     gvc->ghostview_class.next = XmuMakeAtom("NEXT");
  533     gvc->ghostview_class.page = XmuMakeAtom("PAGE");
  534     gvc->ghostview_class.done = XmuMakeAtom("DONE");
  535 }
  536 
  537 /* Initialize private state. */
  538 
  539 static void
  540 Initialize(request, new, args, num_args)
  541     Widget request, new;
  542     ArgList args;       /* unused */
  543     Cardinal *num_args; /* unused */
  544 {
  545     XGCValues   values;
  546     XtGCMask    mask;
  547     GhostviewWidget ngvw = (GhostviewWidget) new;
  548     GhostviewWidget rgvw = (GhostviewWidget) request;
  549 
  550     values.foreground = new->core.background_pixel;
  551     mask = GCForeground;
  552     ngvw->ghostview.gc = XtGetGC(new, mask, &values);
  553     ngvw->ghostview.mwin = None;
  554     ngvw->ghostview.disable_start = False;
  555     ngvw->ghostview.interpreter_pid = -1;
  556     ngvw->ghostview.input_buffer = NULL;
  557     ngvw->ghostview.bytes_left = 0;
  558 #ifndef VMS
  559     ngvw->ghostview.input_buffer_ptr = NULL;
  560     ngvw->ghostview.buffer_bytes_left = 0;
  561 #endif
  562     ngvw->ghostview.ps_input = NULL;
  563     ngvw->ghostview.interpreter_input = -1;
  564     ngvw->ghostview.interpreter_output = -1;
  565 #ifndef VMS
  566     ngvw->ghostview.interpreter_error = -1;
  567     ngvw->ghostview.interpreter_input_id = None;
  568     ngvw->ghostview.interpreter_output_id = None;
  569     ngvw->ghostview.interpreter_error_id = None;
  570 #else /* VMS */
  571     memset(ngvw->ghostview.interpreter_input_iosb, 0, 8);
  572     memset(ngvw->ghostview.interpreter_output_iosb, 0, 8);
  573     ngvw->ghostview.output_buffer = NULL;
  574 #endif /* VMS */
  575     ngvw->ghostview.gs_width = 0;
  576     ngvw->ghostview.gs_height = 0;
  577     ngvw->ghostview.changed = False;
  578     ngvw->ghostview.busy = False;
  579 
  580     /* Compute window size */
  581     Layout(new, (rgvw->core.width == 0), (rgvw->core.height == 0));
  582 }
  583 
  584 /* Create Window and start interpreter if needed */
  585 static void
  586 Realize(w, valueMask, attributes)
  587     Widget w;
  588     Mask *valueMask;
  589     XSetWindowAttributes *attributes;
  590 {
  591     GhostviewWidget gvw = (GhostviewWidget) w;
  592 
  593     if (gvw->ghostview.cursor != None) {
  594     attributes->cursor = gvw->ghostview.cursor;
  595     *valueMask |= CWCursor;
  596     }
  597 
  598     XtCreateWindow(w, (unsigned int) InputOutput, (Visual *) CopyFromParent,
  599            *valueMask, attributes);
  600 
  601     Setup(w);
  602 }
  603 
  604 /* Destroy routine: kill the interpreter and release the GC */
  605 static void
  606 Destroy(w)
  607     Widget w;
  608 {
  609     GhostviewWidget gvw = (GhostviewWidget) w;
  610 
  611     StopInterpreter(w);
  612     XtReleaseGC(w, gvw->ghostview.gc);
  613     if (gvw->ghostview.input_buffer) XtFree(gvw->ghostview.input_buffer);
  614 #ifdef VMS
  615     if (gvw->ghostview.output_buffer) XtFree(gvw->ghostview.output_buffer);
  616 #endif VMS
  617     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
  618     XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  619 }
  620 
  621 /* Process resize request.  Requested size cannot be changed.
  622  * NOTE: This routine may be called before the widget is realized.
  623  * (It was a surprise to me.)
  624  * If the widget is realized, start a new interpreter by calling Setup().
  625  * If Setup() actually started a new interpreter and it is taking input
  626  * from stdin, send a refresh message to the application.  This is the
  627  * only way that the application can be notified that it needs to resend
  628  * the input because someone forced a new window size on the widget.
  629  */
  630 static void
  631 Resize(w)
  632     Widget w;
  633 {
  634     Layout(w, False, False);
  635     if (!XtIsRealized(w)) return;
  636     if (Setup(w)) {
  637     GhostviewWidget gvw = (GhostviewWidget) w;
  638     if (gvw->ghostview.filename == NULL) {
  639         XtCallCallbackList(w, gvw->ghostview.message_callback, "Refresh");
  640     }
  641     }
  642 }
  643 
  644 /* SetValues routine.  Set new private state, based on changed values
  645  * in the widget.  Always returns False, because redisplay is never needed.
  646  */
  647 static Boolean
  648 SetValues(current, request, new)
  649     Widget current, request, new;
  650 {
  651     GhostviewWidget cgvw = (GhostviewWidget) current;
  652     GhostviewWidget rgvw = (GhostviewWidget) request;
  653     GhostviewWidget ngvw = (GhostviewWidget) new;
  654     String cfilename;
  655     String rfilename;
  656     String cpreload;
  657     String rpreload;
  658 
  659     cfilename = cgvw->ghostview.filename;
  660     if (cfilename == NULL) cfilename = "(null)";
  661     rfilename = rgvw->ghostview.filename;
  662     if (rfilename == NULL) rfilename = "(null)";
  663     cpreload = cgvw->ghostview.preload;
  664     if (cpreload == NULL) cpreload = "(null)";
  665     rpreload = rgvw->ghostview.preload;
  666     if (rpreload == NULL) rpreload = "(null)";
  667 
  668     if (XtIsRealized(new) && !ngvw->ghostview.busy &&
  669     (cgvw->ghostview.cursor != ngvw->ghostview.cursor)) {
  670     XDefineCursor(XtDisplay(new), XtWindow(new), ngvw->ghostview.cursor);
  671     }
  672     if (XtIsRealized(new) && ngvw->ghostview.busy &&
  673     (cgvw->ghostview.busy_cursor != ngvw->ghostview.busy_cursor)) {
  674     XDefineCursor(XtDisplay(new), XtWindow(new),
  675               ngvw->ghostview.busy_cursor);
  676     }
  677     if ((cgvw->core.width != rgvw->core.width) ||
  678     (cgvw->core.height != rgvw->core.height) ||
  679     strcmp(cgvw->ghostview.interpreter, rgvw->ghostview.interpreter) ||
  680     strcmp(cpreload, rpreload) ||
  681     (cgvw->ghostview.quiet != rgvw->ghostview.quiet) ||
  682     strcmp(cfilename, rfilename) ||
  683     (cgvw->ghostview.orientation != rgvw->ghostview.orientation) ||
  684     (cgvw->ghostview.use_bpixmap != rgvw->ghostview.use_bpixmap) ||
  685     (cgvw->ghostview.xdpi != rgvw->ghostview.xdpi) ||
  686     (cgvw->ghostview.ydpi != rgvw->ghostview.ydpi) ||
  687     (cgvw->ghostview.bottom_margin != rgvw->ghostview.bottom_margin) ||
  688     (cgvw->ghostview.left_margin != rgvw->ghostview.left_margin) ||
  689     (cgvw->ghostview.right_margin != rgvw->ghostview.right_margin) ||
  690     (cgvw->ghostview.top_margin != rgvw->ghostview.top_margin) ||
  691     (cgvw->ghostview.llx != rgvw->ghostview.llx) ||
  692     (cgvw->ghostview.lly != rgvw->ghostview.lly) ||
  693     (cgvw->ghostview.urx != rgvw->ghostview.urx) ||
  694     (cgvw->ghostview.ury != rgvw->ghostview.ury)) {
  695 
  696     ngvw->ghostview.changed = True;
  697     Layout(new, True, True);
  698     }
  699 
  700     if (ngvw->ghostview.changed && XtIsRealized(current)) Setup(new);
  701     return(False);
  702 }
  703 
  704 /*  Function Name: QueryGeometry
  705  *  Description: This tells the parent what size we would like to be
  706  *                   given certain constraints.
  707  *  Arguments: w - the widget.
  708  *                 intended - what the parent intends to do with us.
  709  *                 requested - what we want to happen.
  710  */
  711 
  712 static XtGeometryResult 
  713 QueryGeometry(w, intended, requested)
  714 Widget w;
  715 XtWidgetGeometry *intended, *requested;
  716 {
  717     Dimension new_width, new_height;
  718     Boolean change, width_req, height_req;
  719     
  720     width_req = intended->request_mode & CWWidth;
  721     height_req = intended->request_mode & CWHeight;
  722 
  723     if (width_req)
  724       new_width = intended->width;
  725     else
  726       new_width = w->core.width;
  727 
  728     if (height_req)
  729       new_height = intended->height;
  730     else
  731       new_height = w->core.height;
  732 
  733     requested->request_mode = 0;
  734     
  735 /*
  736  * We only care about our height and width.
  737  */
  738 
  739     if (!width_req && !height_req)
  740       return(XtGeometryYes);
  741     
  742     change = ComputeSize(w, !width_req, !height_req, &new_width, &new_height);
  743 
  744     requested->request_mode |= CWWidth;
  745     requested->width = new_width;
  746     requested->request_mode |= CWHeight;
  747     requested->height = new_height;
  748 
  749     if (change)
  750         return(XtGeometryAlmost);
  751     return(XtGeometryYes);
  752 }
  753 
  754 /* Layout the widget. */
  755 
  756 static void
  757 Layout(w, xfree, yfree)
  758     Widget w;
  759     Boolean xfree, yfree;
  760 {
  761     Dimension width = w->core.width;
  762     Dimension height = w->core.height;
  763     Boolean different_size = ComputeSize(w, xfree, yfree, &width, &height);
  764     if (different_size) ChangeSize(w, width, height);
  765 }
  766 
  767 /* Compute new size of window, sets xdpi and ydpi if necessary.
  768  * returns True if new window size is different */
  769 static Boolean
  770 ComputeSize(w, xfree, yfree, width, height)
  771     Widget w;
  772     Boolean xfree, yfree;   /* Am I allowed to change width or height */
  773     Dimension *width, *height;
  774 {
  775     GhostviewWidget gvw = (GhostviewWidget) w;
  776     Dimension new_width = *width;
  777     Dimension new_height = *height;
  778     float newxdpi, newydpi;
  779     Boolean change;
  780 
  781     if (xfree && yfree) {
  782     /* width and height can be changed, calculate window size according */
  783     /* to xpdi and ydpi */
  784     switch (gvw->ghostview.orientation) {
  785     case XtPageOrientationPortrait:
  786     case XtPageOrientationUpsideDown:
  787         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  788              gvw->ghostview.xdpi + 0.5;
  789         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  790               gvw->ghostview.ydpi + 0.5;
  791         break;
  792     case XtPageOrientationLandscape:
  793     case XtPageOrientationSeascape:
  794         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  795              gvw->ghostview.xdpi + 0.5;
  796         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  797               gvw->ghostview.ydpi + 0.5;
  798         break;
  799     }
  800     } else if (xfree) {
  801     /* height is fixed.  Preserve aspect ratio by recomputing */
  802     /* ydpi and xdpi */
  803     switch (gvw->ghostview.orientation) {
  804     case XtPageOrientationPortrait:
  805     case XtPageOrientationUpsideDown:
  806         newydpi = gvw->core.height * 72.0 /
  807               (gvw->ghostview.ury - gvw->ghostview.lly);
  808         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  809         gvw->ghostview.xdpi = newxdpi;
  810         gvw->ghostview.ydpi = newydpi;
  811         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  812              gvw->ghostview.xdpi + 0.5;
  813         break;
  814     case XtPageOrientationLandscape:
  815     case XtPageOrientationSeascape:
  816         newydpi = gvw->core.height * 72.0 /
  817               (gvw->ghostview.urx - gvw->ghostview.llx);
  818         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  819         gvw->ghostview.xdpi = newxdpi;
  820         gvw->ghostview.ydpi = newydpi;
  821         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  822              gvw->ghostview.xdpi + 0.5;
  823         break;
  824     }
  825     } else if (yfree) {
  826     /* width is fixed.  Preserve aspect ratio by recomputing */
  827     /* xdpi and ydpi */
  828     switch (gvw->ghostview.orientation) {
  829     case XtPageOrientationPortrait:
  830     case XtPageOrientationUpsideDown:
  831         newxdpi = gvw->core.width * 72.0 /
  832               (gvw->ghostview.urx - gvw->ghostview.llx);
  833         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  834         gvw->ghostview.xdpi = newxdpi;
  835         gvw->ghostview.ydpi = newydpi;
  836         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  837               gvw->ghostview.ydpi + 0.5;
  838         break;
  839     case XtPageOrientationLandscape:
  840     case XtPageOrientationSeascape:
  841         newxdpi = gvw->core.width * 72.0 /
  842               (gvw->ghostview.ury - gvw->ghostview.lly);
  843         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  844         gvw->ghostview.xdpi = newxdpi;
  845         gvw->ghostview.ydpi = newydpi;
  846         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  847               gvw->ghostview.ydpi + 0.5;
  848         break;
  849     }
  850     } else {
  851     /* height and width are fixed.  Just have to live with it. */
  852     switch (gvw->ghostview.orientation) {
  853     case XtPageOrientationPortrait:
  854     case XtPageOrientationUpsideDown:
  855         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  856                   (gvw->ghostview.urx - gvw->ghostview.llx);
  857         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  858                   (gvw->ghostview.ury - gvw->ghostview.lly);
  859         break;
  860     case XtPageOrientationLandscape:
  861     case XtPageOrientationSeascape:
  862         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  863                   (gvw->ghostview.ury - gvw->ghostview.lly);
  864         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  865                   (gvw->ghostview.urx - gvw->ghostview.llx);
  866         break;
  867     }
  868     }
  869 
  870     change = (new_width != *width) || (new_height != *height);
  871     *width = new_width;
  872     *height = new_height;
  873     return (change);
  874 }
  875 
  876 /*  Function Name: ChangeSize.
  877  *  Description: Request a size change.
  878  *  Arguments: w - the widget to try change the size of.
  879  */
  880 
  881 static void
  882 ChangeSize(w, width, height)
  883 Widget w;
  884 Dimension width, height;
  885 {
  886     XtWidgetGeometry request, reply;
  887     Boolean changed = False;
  888 
  889     request.request_mode = CWWidth | CWHeight;
  890     request.width = width;
  891     request.height = height;
  892     
  893     switch ( XtMakeGeometryRequest(w, &request, &reply) ) {
  894     case XtGeometryYes:
  895     changed = True;
  896         break;
  897     case XtGeometryNo:
  898         break;
  899     case XtGeometryAlmost:
  900     ComputeSize(w, (request.height != reply.height),
  901                (request.width != reply.width),
  902                &(reply.width), &(reply.height));
  903     request = reply;
  904     switch (XtMakeGeometryRequest(w, &request, &reply) ) {
  905     case XtGeometryYes:
  906         changed = True;
  907         break;
  908     case XtGeometryNo:
  909         break;
  910     case XtGeometryAlmost:
  911         request = reply;
  912         ComputeSize(w, FALSE, FALSE, &(request.width), &(request.height));
  913         request.request_mode = CWWidth | CWHeight;
  914         XtMakeGeometryRequest(w, &request, &reply);
  915         changed = True;
  916         break;
  917     }
  918     break;
  919     }
  920 
  921     /* If success, setup the widet for the new size. */
  922     if (changed && XtIsRealized(w)) Setup(w);
  923 }
  924 
  925 /* Catch the alloc error when there is not enough resources for the
  926  * backing pixmap.  Automatically shut off backing pixmap and let the
  927  * user know when this happens.
  928  */
  929 static Boolean alloc_error;
  930 static XErrorHandler oldhandler;
  931 
  932 static int
  933 catch_alloc (dpy, err)
  934 Display *dpy;
  935 XErrorEvent *err;
  936 {
  937     if (err->error_code == BadAlloc) {
  938     alloc_error = True;
  939     }
  940     if (alloc_error) return 0;
  941     oldhandler(dpy, err);
  942 }
  943 
  944 /* Setup - sets up the backing pixmap, and GHOSTVIEW property and
  945  * starts interpreter if needed.
  946  * NOTE: the widget must be realized before calling Setup().
  947  * Returns True if a new interpreter was started, False otherwise.
  948  */
  949  
  950 static Boolean
  951 Setup(w)
  952     Widget w;
  953 {
  954     GhostviewWidget gvw = (GhostviewWidget) w;
  955     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  956     char buf[GV_BUFSIZ];
  957     Pixmap bpixmap;
  958     XSetWindowAttributes xswa;
  959 
  960     if (!gvw->ghostview.changed &&
  961     (gvw->core.width == gvw->ghostview.gs_width) &&
  962     (gvw->core.height == gvw->ghostview.gs_height)) return False;
  963 
  964     StopInterpreter(w);
  965 
  966     if ((gvw->core.width != gvw->ghostview.gs_width) ||
  967     (gvw->core.height != gvw->ghostview.gs_height) ||
  968     (!gvw->ghostview.use_bpixmap)) {
  969     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  970         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  971         gvw->core.background_pixmap = XtUnspecifiedPixmap;
  972         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
  973     }
  974     }
  975 
  976     if (gvw->ghostview.use_bpixmap) {
  977     if (gvw->core.background_pixmap == XtUnspecifiedPixmap) {
  978         /* Get a Backing Pixmap, but be ready for the BadAlloc. */
  979         XSync(XtDisplay(w), False);  /* Get to known state */
  980         oldhandler = XSetErrorHandler(catch_alloc);
  981         alloc_error = False;
  982         bpixmap = XCreatePixmap(XtDisplay(w), XtWindow(w),
  983                     gvw->core.width, gvw->core.height,
  984                     gvw->core.depth);
  985         XSync(XtDisplay(w), False);  /* Force the error */
  986         if (alloc_error) {
  987         XtCallCallbackList(w, gvw->ghostview.message_callback,
  988                    "BadAlloc");
  989         if (bpixmap != None) {
  990             XFreePixmap(XtDisplay(w), bpixmap);
  991             XSync(XtDisplay(w), False);  /* Force the error */
  992             bpixmap = None;
  993         }
  994         }
  995         oldhandler = XSetErrorHandler(oldhandler);
  996         if (bpixmap != None) {
  997         gvw->core.background_pixmap = bpixmap;
  998         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w),
  999                        gvw->core.background_pixmap);
 1000         }
 1001     } else {
 1002         bpixmap = gvw->core.background_pixmap;
 1003     }
 1004     } else {
 1005     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
 1006         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
 1007         gvw->core.background_pixmap = XtUnspecifiedPixmap;
 1008         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
 1009     }
 1010     bpixmap = None;
 1011     }
 1012 
 1013     if (bpixmap != None) {
 1014     xswa.backing_store = NotUseful;
 1015     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
 1016                 CWBackingStore, &xswa);
 1017     } else {
 1018     xswa.backing_store = Always;
 1019     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
 1020                 CWBackingStore, &xswa);
 1021     }
 1022 
 1023     gvw->ghostview.gs_width = gvw->core.width;
 1024     gvw->ghostview.gs_height = gvw->core.height;
 1025 
 1026     sprintf(buf, "%d %d %d %d %d %d %g %g %d %d %d %d",
 1027         bpixmap, gvw->ghostview.orientation,
 1028         gvw->ghostview.llx, gvw->ghostview.lly,
 1029         gvw->ghostview.urx, gvw->ghostview.ury,
 1030         gvw->ghostview.xdpi, gvw->ghostview.ydpi,
 1031         gvw->ghostview.left_margin, gvw->ghostview.bottom_margin,
 1032         gvw->ghostview.right_margin, gvw->ghostview.top_margin);
 1033     XChangeProperty(XtDisplay(w), XtWindow(w),
 1034            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.ghostview),
 1035            XA_STRING, 8, PropModeReplace,
 1036            (unsigned char *)buf, strlen(buf));
 1037 
 1038     StartInterpreter(w);
 1039     return True;
 1040 }
 1041 
 1042 #ifndef VMS
 1043 
 1044 /* This routine starts the interpreter.  It sets the DISPLAY and 
 1045  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
 1046  * contains the Window that ghostscript should write on.
 1047  *
 1048  * This routine also opens pipes for stdout and stderr and initializes
 1049  * application input events for them.  If input to ghostscript is not
 1050  * from a file, a pipe for stdin is created.  This pipe is setup for
 1051  * non-blocking I/O so that the user interface never "hangs" because of
 1052  * a write to ghostscript.
 1053  */
 1054 static void
 1055 StartInterpreter(w)
 1056     Widget w;
 1057 {
 1058     GhostviewWidget gvw = (GhostviewWidget) w;
 1059     int std_in[2];
 1060     int std_out[2];
 1061     int std_err[2];
 1062     char buf[GV_BUFSIZ];
 1063 #define NUM_ARGS 100
 1064     char *argv[NUM_ARGS];
 1065     char *preload = NULL;
 1066     char *cptr;
 1067     int argc = 0;
 1068     int ret;
 1069 
 1070     StopInterpreter(w);
 1071 
 1072     /* Clear the window before starting a new interpreter. */
 1073     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
 1074     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
 1075                gvw->ghostview.gc,
 1076                0, 0, gvw->core.width, gvw->core.height);
 1077     }
 1078     XClearArea(XtDisplay(w), XtWindow(w),
 1079            0, 0, gvw->core.width, gvw->core.height, False);
 1080 
 1081     if (gvw->ghostview.disable_start) return;
 1082 
 1083     argv[argc++] = gvw->ghostview.interpreter;
 1084     if (gvw->ghostview.quiet) argv[argc++] = "-dQUIET";
 1085     argv[argc++] = "-dNOPAUSE";
 1086     if (gvw->ghostview.preload) {
 1087     cptr = preload = XtNewString(gvw->ghostview.preload);
 1088     while (isspace(*cptr)) cptr++;
 1089     while (*cptr) {
 1090         argv[argc++] = cptr;
 1091         while (*cptr && !isspace(*cptr)) cptr++;
 1092         *cptr++ = '\0';
 1093         if (argc + 2 >= NUM_ARGS) {
 1094         fprintf(stderr, "Too many files to preload.\n");
 1095         exit(1);
 1096         }
 1097         while (isspace(*cptr)) cptr++;
 1098     }
 1099     }
 1100     argv[argc++] = "-";
 1101     argv[argc++] = NULL;
 1102 
 1103     if (gvw->ghostview.filename == NULL) {
 1104     ret = pipe(std_in);
 1105     if (ret == -1) {
 1106         perror("Could not create pipe");
 1107         exit(1);
 1108     }
 1109     } else if (strcmp(gvw->ghostview.filename, "-")) {
 1110     std_in[0] = open(gvw->ghostview.filename, O_RDONLY, 0);
 1111     }
 1112     ret = pipe(std_out);
 1113     if (ret == -1) {
 1114     perror("Could not create pipe");
 1115     exit(1);
 1116     }
 1117     ret = pipe(std_err);
 1118     if (ret == -1) {
 1119     perror("Could not create pipe");
 1120     exit(1);
 1121     }
 1122 
 1123     gvw->ghostview.changed = False;
 1124     gvw->ghostview.busy = True;
 1125     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
 1126 #if defined(SYSV) || defined(USG)
 1127 #define vfork fork
 1128 #endif
 1129     gvw->ghostview.interpreter_pid = vfork();
 1130 
 1131     if (gvw->ghostview.interpreter_pid == 0) { /* child */
 1132     close(std_out[0]);
 1133     close(std_err[0]);
 1134     dup2(std_out[1], 1);
 1135     close(std_out[1]);
 1136     dup2(std_err[1], 2);
 1137     close(std_err[1]);
 1138     sprintf(buf, "%d", XtWindow(w));
 1139     setenv("GHOSTVIEW", buf, True);
 1140     setenv("DISPLAY", XDisplayString(XtDisplay(w)), True);
 1141     if (gvw->ghostview.filename == NULL) {
 1142         close(std_in[1]);
 1143         dup2(std_in[0], 0);
 1144         close(std_in[0]);
 1145     } else if (strcmp(gvw->ghostview.filename, "-")) {
 1146         dup2(std_in[0], 0);
 1147         close(std_in[0]);
 1148     }
 1149     execvp(argv[0], argv);
 1150     sprintf(buf, "Exec of %s failed", argv[0]);
 1151     perror(buf);
 1152     _exit(1);
 1153     } else {
 1154     if (gvw->ghostview.filename == NULL) {
 1155 #ifdef NON_BLOCKING_IO
 1156         int result;
 1157 #endif
 1158         close(std_in[0]);
 1159 
 1160 #ifdef NON_BLOCKING_IO
 1161         result = fcntl(std_in[1], F_GETFL, 0);
 1162         result = result | O_NONBLOCK;
 1163         result = fcntl(std_in[1], F_SETFL, result);
 1164 #endif
 1165         gvw->ghostview.interpreter_input = std_in[1];
 1166         gvw->ghostview.interpreter_input_id = None;
 1167     } else if (strcmp(gvw->ghostview.filename, "-")) {
 1168         close(std_in[0]);
 1169     }
 1170     close(std_out[1]);
 1171     gvw->ghostview.interpreter_output = std_out[0];
 1172     gvw->ghostview.interpreter_output_id = 
 1173         XtAppAddInput(XtWidgetToApplicationContext(w), std_out[0],
 1174               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
 1175     close(std_err[1]);
 1176     gvw->ghostview.interpreter_error = std_err[0];
 1177     gvw->ghostview.interpreter_error_id = 
 1178         XtAppAddInput(XtWidgetToApplicationContext(w), std_err[0],
 1179               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
 1180     }
 1181     if (preload) XtFree(preload);
 1182 }
 1183 
 1184 /* Stop the interperter, if present, and remove any Input sources. */
 1185 /* Also reset the busy state. */
 1186 static void
 1187 StopInterpreter(w)
 1188     Widget w;
 1189 {
 1190     GhostviewWidget gvw = (GhostviewWidget) w;
 1191     if (gvw->ghostview.interpreter_pid >= 0) {
 1192     kill(gvw->ghostview.interpreter_pid, SIGTERM);
 1193     wait(0);
 1194     gvw->ghostview.interpreter_pid = -1;
 1195     }
 1196     if (gvw->ghostview.interpreter_input >= 0) {
 1197     close(gvw->ghostview.interpreter_input);
 1198     gvw->ghostview.interpreter_input = -1;
 1199     if (gvw->ghostview.interpreter_input_id != None) {
 1200         XtRemoveInput(gvw->ghostview.interpreter_input_id);
 1201         gvw->ghostview.interpreter_input_id = None;
 1202     }
 1203     while (gvw->ghostview.ps_input) {
 1204         struct record_list *ps_old = gvw->ghostview.ps_input;
 1205         gvw->ghostview.ps_input = ps_old->next;
 1206         if (ps_old->close) fclose(ps_old->fp);
 1207         XtFree((char *)ps_old);
 1208     }
 1209     }
 1210     if (gvw->ghostview.interpreter_output >= 0) {
 1211     close(gvw->ghostview.interpreter_output);
 1212     gvw->ghostview.interpreter_output = -1;
 1213     XtRemoveInput(gvw->ghostview.interpreter_output_id);
 1214     }
 1215     if (gvw->ghostview.interpreter_error >= 0) {
 1216     close(gvw->ghostview.interpreter_error);
 1217     gvw->ghostview.interpreter_error = -1;
 1218     XtRemoveInput(gvw->ghostview.interpreter_error_id);
 1219     }
 1220     gvw->ghostview.busy = False;
 1221     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
 1222 }
 1223 
 1224 #endif /* VMS */
 1225 
 1226 /* The interpeter failed, Stop what's left and notify application */
 1227 static void
 1228 InterpreterFailed(w)
 1229     Widget w;
 1230 {
 1231     GhostviewWidget gvw = (GhostviewWidget) w;
 1232     StopInterpreter(w);
 1233     XtCallCallbackList(w, gvw->ghostview.message_callback, "Failed");
 1234 }
 1235 
 1236 /*
 1237  *  Public Routines
 1238  */
 1239 
 1240 /* GhostviewDisableInterpreter:
 1241  * Stop any interpreter and disable new ones from starting.
 1242  */
 1243 void
 1244 GhostviewDisableInterpreter(w)
 1245     Widget w;
 1246 {
 1247     GhostviewWidget gvw = (GhostviewWidget) w;
 1248     gvw->ghostview.disable_start = True;
 1249     if (XtIsRealized(w)) StopInterpreter(w);
 1250 }
 1251 
 1252 /* GhostviewDisableInterpreter:
 1253  * Allow an interpreter to start and start one if the widget is
 1254  * currently realized.
 1255  */
 1256 void
 1257 GhostviewEnableInterpreter(w)
 1258     Widget w;
 1259 {
 1260     GhostviewWidget gvw = (GhostviewWidget) w;
 1261     gvw->ghostview.disable_start = False;
 1262     if (XtIsRealized(w)) StartInterpreter(w);
 1263 }
 1264 
 1265 /* GhostviewIsInterpreterReady:
 1266  * Returns True if the interpreter is ready for new input.
 1267  */
 1268 Boolean
 1269 GhostviewIsInterpreterReady(w)
 1270     Widget w;
 1271 {
 1272     GhostviewWidget gvw = (GhostviewWidget) w;
 1273     return gvw->ghostview.interpreter_pid != -1 &&
 1274        !gvw->ghostview.busy &&
 1275        gvw->ghostview.ps_input == NULL;
 1276 }
 1277 
 1278 /* GhostviewIsInterpreterRunning:
 1279  * Returns True if the interpreter is running.
 1280  */
 1281 Boolean
 1282 GhostviewIsInterpreterRunning(w)
 1283     Widget w;
 1284 {
 1285     GhostviewWidget gvw = (GhostviewWidget) w;
 1286     return gvw->ghostview.interpreter_pid != -1;
 1287 }
 1288 
 1289 /* GhostviewGetBackingPixmap:
 1290  * Returns the current backing pixmap.
 1291  */
 1292 Pixmap
 1293 GhostviewGetBackingPixmap(w)
 1294     Widget w;
 1295 {
 1296     GhostviewWidget gvw = (GhostviewWidget) w;
 1297     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
 1298     return(gvw->core.background_pixmap);
 1299     else
 1300     return(None);
 1301 }
 1302 
 1303 #ifndef VMS
 1304 
 1305 /* GhostviewSendPS:
 1306  *   Queue a portion of a PostScript file for output to ghostscript.
 1307  *   fp: FILE * of the file in question.  NOTE: if you have several
 1308  *   Ghostview widgets reading from the same file.  You must open
 1309  *   a unique FILE * for each widget.
 1310  *   SendPS does not actually send the PostScript, it merely queues it
 1311  *   for output.
 1312  *   begin: position in file (returned from ftell()) to start.
 1313  *   len:   number of bytes to write.
 1314  *
 1315  *   If an interpreter is not running, nothing is queued and
 1316  *   False is returned.
 1317  */
 1318 Boolean
 1319 GhostviewSendPS(w, fp, begin, len, close)
 1320     Widget w;
 1321     FILE *fp;
 1322     long begin;
 1323     unsigned int len;
 1324     Bool close;
 1325 {
 1326     GhostviewWidget gvw = (GhostviewWidget) w;
 1327     struct record_list *ps_new;
 1328 
 1329     if (gvw->ghostview.interpreter_input < 0) return False;
 1330     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
 1331     ps_new->fp = fp;
 1332     ps_new->begin = begin;
 1333     ps_new->len = len;
 1334     ps_new->seek_needed = True;
 1335     ps_new->close = close;
 1336     ps_new->next = NULL;
 1337 
 1338     if (gvw->ghostview.input_buffer == NULL) {
 1339     gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
 1340     }
 1341 
 1342     if (gvw->ghostview.ps_input == NULL) {
 1343     gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
 1344     gvw->ghostview.bytes_left = len;
 1345     gvw->ghostview.buffer_bytes_left = 0;
 1346     gvw->ghostview.ps_input = ps_new;
 1347     gvw->ghostview.interpreter_input_id =
 1348         XtAppAddInput(XtWidgetToApplicationContext(w),
 1349                   gvw->ghostview.interpreter_input,
 1350                   (XtPointer)XtInputWriteMask, Input, (XtPointer)w);
 1351     } else {
 1352     struct record_list *p = gvw->ghostview.ps_input;
 1353     while (p->next != NULL) {
 1354         p = p->next;
 1355     }
 1356     p->next = ps_new;
 1357     }
 1358     return True;
 1359 }
 1360 
 1361 #endif /* VMS */
 1362 
 1363 /* GhostviewNextPage:
 1364  *   Tell ghostscript to start the next page.
 1365  *   Returns False if ghostscript is not running, or not ready to start
 1366  *   another page.
 1367  *   If another page is started.  Sets the busy flag and cursor.
 1368  */
 1369 Boolean
 1370 GhostviewNextPage(w)
 1371     Widget w;
 1372 {
 1373     GhostviewWidget gvw = (GhostviewWidget) w;
 1374     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
 1375     XEvent event;
 1376 
 1377     if (gvw->ghostview.interpreter_pid < 0) return False;
 1378     if (gvw->ghostview.mwin == None) return False;
 1379 
 1380     if (!gvw->ghostview.busy) {
 1381     gvw->ghostview.busy = True;
 1382     XDefineCursor(XtDisplay(gvw), XtWindow(gvw),
 1383               gvw->ghostview.busy_cursor);
 1384 
 1385     event.xclient.type = ClientMessage;
 1386     event.xclient.display = XtDisplay(w);
 1387     event.xclient.window = gvw->ghostview.mwin;
 1388     event.xclient.message_type =
 1389         XmuInternAtom(XtDisplay(w), gvc->ghostview_class.next);
 1390     event.xclient.format = 32;
 1391     XSendEvent(XtDisplay(w), gvw->ghostview.mwin, False, 0, &event);
 1392     return True;
 1393     } else {
 1394     return False;
 1395     }
 1396 }
 1397 
 1398 #define done(type, value) \
 1399     {                           \
 1400         if (toVal->addr != NULL) {              \
 1401         if (toVal->size < sizeof(type)) {       \
 1402             toVal->size = sizeof(type);         \
 1403             return False;               \
 1404         }                       \
 1405         *(type*)(toVal->addr) = (value);        \
 1406         }                           \
 1407         else {                      \
 1408         static type static_val;             \
 1409         static_val = (value);               \
 1410         toVal->addr = (XPointer)&static_val;        \
 1411         }                           \
 1412         toVal->size = sizeof(type);             \
 1413         return True;                    \
 1414     }
 1415 
 1416 /* PageOrienation Conversion Routine.
 1417  * Returns True if Conversion is successful.
 1418  */
 1419 Boolean
 1420 XmuCvtStringToPageOrientation(dpy, args, num_args, fromVal, toVal, data)
 1421     Display *dpy;
 1422     XrmValue    *args;      /* unused */
 1423     Cardinal    *num_args;  /* unused */
 1424     XrmValue    *fromVal;
 1425     XrmValue    *toVal;
 1426     XtPointer   *data;      /* unused */
 1427 {
 1428     static XrmQuark     XrmQEportrait;
 1429     static XrmQuark     XrmQElandscape;
 1430     static XrmQuark     XrmQEupsideDown;
 1431     static XrmQuark     XrmQEseascape;
 1432     static int          haveQuarks;
 1433     XrmQuark    q;
 1434     char    *str = (XPointer) fromVal->addr;
 1435     char        lowerName[1000];
 1436 
 1437     if (str == NULL) return False;
 1438 
 1439     if (!haveQuarks) {
 1440     XrmQEportrait   = XrmStringToQuark(XtEportrait);
 1441     XrmQElandscape  = XrmStringToQuark(XtElandscape);
 1442     XrmQEupsideDown = XrmStringToQuark(XtEupsideDown);
 1443     XrmQEseascape   = XrmStringToQuark(XtEseascape);
 1444     haveQuarks = 1;
 1445     }
 1446 
 1447     XmuCopyISOLatin1Lowered(lowerName, str);
 1448 
 1449     q = XrmStringToQuark(lowerName);
 1450 
 1451     if (q == XrmQEportrait)
 1452     done(XtPageOrientation, XtPageOrientationPortrait);
 1453     if (q == XrmQElandscape)
 1454     done(XtPageOrientation, XtPageOrientationLandscape);
 1455     if (q == XrmQEupsideDown)
 1456     done(XtPageOrientation, XtPageOrientationUpsideDown);
 1457     if (q == XrmQEseascape)
 1458     done(XtPageOrientation, XtPageOrientationSeascape);
 1459 
 1460     XtDisplayStringConversionWarning(dpy, str, XtRPageOrientation);
 1461     return False;
 1462 }
 1463 
 1464 #ifdef VMS
 1465 
 1466 /*
 1467 ** VMS specific include files
 1468 */
 1469 #include <descrip.h>
 1470 #include <ssdef.h>
 1471 #include <clidef.h>
 1472 #include <lnmdef.h>
 1473 #include <iodef.h>
 1474 #include <dvidef.h>
 1475 #include "vms_types.h"
 1476 
 1477 #define ERR_SIGNAL(s) if(!((s) & 1))lib$signal((s), 0, 0)
 1478 #define XtEFN 23
 1479 
 1480 struct g_l_i
 1481 {
 1482     GhostviewWidget w;
 1483     struct g_l_i *next;
 1484 };
 1485 
 1486 typedef struct g_l_i GhostListItem, *GLI_p;
 1487 
 1488 static GhostListItem glhead = {(GhostviewWidget) -1, NULL};
 1489 static GLI_p GL = &glhead;
 1490 static size_t GLI_Size = sizeof(GhostListItem);
 1491 static XtInputId EventId;
 1492 
 1493 /*
 1494 ** This routine is passed to XtAppAddInput(). It is called whenever the event
 1495 ** flag number XtEFN is set and the Xt main loop becomes idle. It clears the
 1496 ** event flag and then scans all the ghostview widgets for completed I/O
 1497 ** requests, processing each as they are found. We have to do them all because
 1498 ** there is no way to have Xt dispatch them individually without a window of
 1499 ** vulnerability that can cause missed events, or by using a separate event
 1500 ** flag for each I/O stream. Event flags are, unfortunately, a limited
 1501 ** resource.
 1502 */
 1503 static Boolean
 1504 IOProcess()
 1505 {
 1506     GhostviewWidget gvw;
 1507     GLI_p cur;
 1508 
 1509     /*
 1510     ** Before we process any I/O's, clear the event flag.
 1511     */
 1512     sys$clref(XtEFN);
 1513     /*
 1514     ** Scan all the ghostview widgets and check for completed I/O's
 1515     */
 1516     for(cur = GL->next; cur; cur = cur->next){
 1517     /*
 1518     ** Get the widget and check for I/O complete on either mailbox.
 1519     */
 1520     gvw = cur->w;
 1521     if(gvw->ghostview.interpreter_input_iosb[0])Input(gvw);
 1522     if(gvw->ghostview.interpreter_output_iosb[0])Output(gvw);
 1523     }
 1524 }
 1525     
 1526 /*
 1527 ** This is an AST routine. It is called asynchronously whenever one of our
 1528 ** mailbox I/O's completes.
 1529 */
 1530 static void
 1531 IOComplete(client_data)
 1532     XtPointer client_data;
 1533 {
 1534     /*
 1535     ** Set the event flag to tell Xt to call IOProcess.
 1536     */
 1537     sys$setef(XtEFN);
 1538 }
 1539 
 1540 static void
 1541 GLInsert(w)
 1542     GhostviewWidget w;
 1543 {
 1544     GLI_p new;
 1545     int first;
 1546     
 1547     /*
 1548     ** Insert this widget after the list head
 1549     */
 1550     first = (GL->next == NULL);
 1551     new = XtMalloc(GLI_Size);
 1552     new->w = w;
 1553     new->next = GL->next;
 1554     GL->next = new;
 1555     /*
 1556     ** If this is the first item on the list, call XtAppAddInput()
 1557     */
 1558     if(first)EventId = XtAppAddInput(XtWidgetToApplicationContext(w), XtEFN, 0, 
 1559     IOProcess, 0);
 1560 }
 1561 
 1562 static void
 1563 GLRemove(w)
 1564     GhostviewWidget w;
 1565 {
 1566     GLI_p prev, cur;
 1567     int last = 0;
 1568 
 1569     /*
 1570     ** Find and remove this widget from the list.
 1571     */
 1572     prev = GL;
 1573     cur = prev->next;
 1574     while(cur && cur->w != w){
 1575     prev = cur;
 1576     cur = cur->next;
 1577     }
 1578     if(cur){
 1579        prev->next = cur->next;
 1580        XtFree(cur);
 1581        last = (GL->next == NULL);
 1582     }
 1583     /*
 1584     ** If this was the last item on the list, call XtRemoveInput()
 1585     */
 1586     if(last)XtRemoveInput(EventId);
 1587 }
 1588 
 1589 /* Input - Feed data to ghostscript's stdin.
 1590  * Write bytes to ghostscript using non-blocking I/O.
 1591  * Also, pipe signals are caught during writing.  The return
 1592  * values are checked and the appropriate action is taken.  I do
 1593  * this at this low level, because it may not be appropriate for
 1594  * SIGPIPE to be caught for the overall application.
 1595  */
 1596 
 1597 static void
 1598 Input(gvw)
 1599     GhostviewWidget gvw;
 1600 {
 1601     int stat, bbytes;
 1602     char *ch;
 1603 
 1604     /*
 1605     ** Check for error on previous I/O.
 1606     */
 1607     stat = gvw->ghostview.interpreter_input_iosb[0];
 1608     if(stat != SS$_NORMAL){
 1609     InterpreterFailed(gvw);
 1610     } else {
 1611 
 1612     /* Get a new section if required */
 1613     
 1614     if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
 1615         struct record_list *ps_old = gvw->ghostview.ps_input;
 1616         gvw->ghostview.ps_input = ps_old->next;
 1617         if (ps_old->close) fclose(ps_old->fp);
 1618         XtFree((char *)ps_old);
 1619     }
 1620     if(gvw->ghostview.ps_input){
 1621         /* Have to seek at the beginning of each section */
 1622         if (gvw->ghostview.ps_input->seek_needed) {
 1623         if (gvw->ghostview.ps_input->len > 0)
 1624             fseek(gvw->ghostview.ps_input->fp,
 1625               gvw->ghostview.ps_input->begin, SEEK_SET);
 1626         gvw->ghostview.ps_input->seek_needed = False;
 1627         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
 1628         }
 1629         /*
 1630         ** Read a line from the file.
 1631         */
 1632         ch = fgets(gvw->ghostview.input_buffer, GV_BUFSIZ,
 1633         gvw->ghostview.ps_input->fp);
 1634         if(!ch){
 1635         /*
 1636         ** Error, EOF when there's supposed to be data left. 
 1637         */
 1638         InterpreterFailed(gvw);
 1639         } else {
 1640         /*
 1641         ** Write it to the mailbox.
 1642         */
 1643         bbytes = strlen(gvw->ghostview.input_buffer);
 1644         gvw->ghostview.bytes_left -= bbytes;
 1645         stat = sys$qio(0, (short)gvw->ghostview.interpreter_input,
 1646             IO$_WRITEVBLK, &gvw->ghostview.interpreter_input_iosb,
 1647             IOComplete, 0, gvw->ghostview.input_buffer, bbytes,
 1648             0, 0, 0, 0);
 1649         ERR_SIGNAL(stat);
 1650         }
 1651     }
 1652     }
 1653 }
 1654 
 1655 /* Output - receive I/O from ghostscript's stdout and stderr.
 1656  * Pass this to the application via the output_callback. */
 1657 static void
 1658 Output(gvw)
 1659     GhostviewWidget gvw;
 1660 {
 1661     char buf[GV_BUFSIZ+1];
 1662     int bytes, stat;
 1663 
 1664     stat = gvw->ghostview.interpreter_output_iosb[0];
 1665     bytes = gvw->ghostview.interpreter_output_iosb[1];
 1666     if (stat == SS$_NORMAL) {
 1667     /*
 1668     ** Got a message. If line complete, pass to the output_callback.
 1669     **
 1670     ** HACK ALERT, if bytes is -1 nothing happens, but an I/O is queued.
 1671     ** This is our first time code, since Xt doesn't queue the I/O for us
 1672     ** under VMS, just watches for completion. In StartInterpreter We setup
 1673     ** an IOSB with a success status and -1 bytes so Xt will call us the
 1674     ** first time to get the I/O queued.
 1675     */
 1676     if (bytes == 0) {
 1677         strcpy(buf, "\n");
 1678     } else if (bytes == 1) {
 1679         buf[0] = gvw->ghostview.output_buffer[0];
 1680         buf[1] = '\0';
 1681     } else if (bytes > 1) {
 1682         /*
 1683         ** Copy the message to a local buffer and pass it to the callback.
 1684         */
 1685         memcpy(buf, gvw->ghostview.output_buffer, bytes);
 1686         buf[bytes] = '\0';
 1687     }
 1688     if(bytes >= 0)XtCallCallbackList(gvw, gvw->ghostview.output_callback,
 1689         (XtPointer) buf);
 1690     /*
 1691     ** Queue a new read to the mailbox
 1692     */
 1693     stat = sys$qio(0, (short)gvw->ghostview.interpreter_output,
 1694         IO$_READVBLK, &gvw->ghostview.interpreter_output_iosb, IOComplete,
 1695         0, gvw->ghostview.output_buffer, GV_BUFSIZ, 0, 0, 0, 0);
 1696     ERR_SIGNAL(stat);
 1697     } else {
 1698     InterpreterFailed(gvw);     /* Something bad happened */
 1699     }
 1700 }
 1701 
 1702 /* This routine starts the interpreter.  It sets the DISPLAY and 
 1703  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
 1704  * contains the Window that ghostscript should write on.
 1705  *
 1706  * This routine also opens pipes for stdout and stderr and initializes
 1707  * application input events for them.  If input to ghostscript is not
 1708  * from a file, a pipe for stdin is created.  This pipe is setup for
 1709  * non-blocking I/O so that the user interface never "hangs" because of
 1710  * a write to ghostscript.
 1711  */
 1712 static void
 1713 StartInterpreter(w)
 1714     Widget w;
 1715 {
 1716     GhostviewWidget gvw = (GhostviewWidget) w;
 1717     char buf[GV_BUFSIZ];
 1718     char cmd[512];
 1719     int ret;
 1720     short ch1, ch2;
 1721     char in_mbx_name[65], out_mbx_name[65];
 1722     long pid, nowait = CLI$M_NOWAIT;
 1723     const $DESCRIPTOR(ghostview_desc, "GHOSTVIEW");
 1724     const $DESCRIPTOR(display_desc, "DECW$DISPLAY");
 1725     const $DESCRIPTOR(lnt_desc, "LNM$PROCESS");
 1726     $DESCRIPTOR(in_desc, "");
 1727     $DESCRIPTOR(out_desc, "");
 1728     $DESCRIPTOR(lnm_desc, "");
 1729     $DESCRIPTOR(cmd_desc, cmd);
 1730     ITEM_LIST_3_T(gv_list, 1) = {{{0, LNM$_STRING, buf, NULL}}, 0};
 1731     ITEM_LIST_3_T(dis_list, 1) = {{{0, LNM$_STRING, NULL, NULL}}, 0};
 1732     ITEM_LIST_3_T(dvi_list, 1) = {{{64, DVI$_DEVNAM, NULL, NULL}}, 0};
 1733     IOSB_GET_T dvi_iosb;
 1734 
 1735     /*
 1736     ** Stop interpreter if running
 1737     */
 1738     StopInterpreter(w);
 1739     /*
 1740     ** Clear the window before starting a new interpreter.
 1741     */
 1742     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
 1743     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
 1744                gvw->ghostview.gc,
 1745                0, 0, gvw->core.width, gvw->core.height);
 1746     }
 1747     XClearArea(XtDisplay(w), XtWindow(w),
 1748            0, 0, gvw->core.width, gvw->core.height, False);
 1749     /*
 1750     ** Check for disabled.
 1751     */
 1752     if (gvw->ghostview.disable_start) return;
 1753     /*
 1754     ** Build Ghostscript startup command
 1755     */
 1756     strcpy(cmd, gvw->ghostview.interpreter);
 1757     strcat(cmd, " ");
 1758     if (gvw->ghostview.quiet) strcat(cmd, "\"-dQUIET\" ");
 1759     strcat(cmd, "\"-dNOPAUSE\" ");
 1760     if (gvw->ghostview.preload) {
 1761     strcat(cmd, gvw->ghostview.preload);
 1762     strcat(cmd, " ");
 1763     }
 1764     strcat(cmd, "\"-\" ");
 1765 
 1766     /*
 1767     ** Determine input source.
 1768     */
 1769     if (gvw->ghostview.filename == NULL) {
 1770     /*
 1771     ** Create a mailbox to feed input to Ghostscript and get its name.
 1772     */
 1773     ret = sys$crembx(0, &ch1, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
 1774     ERR_SIGNAL(ret);
 1775     dvi_list.item[0].buffer_p = in_mbx_name;
 1776     ret = sys$getdvi(0, ch1, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
 1777     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
 1778     in_mbx_name[64] = '\0';
 1779     in_desc.dsc$a_pointer = in_mbx_name;
 1780     in_desc.dsc$w_length = strlen(in_mbx_name);
 1781     } else {
 1782     /*
 1783     ** Set up file name to give Ghostscript as standard input.
 1784     */
 1785     in_desc.dsc$a_pointer = gvw->ghostview.filename;
 1786     in_desc.dsc$w_length = strlen(gvw->ghostview.filename);
 1787     }
 1788     /*
 1789     ** Create mailbox to receive Ghostscript's output
 1790     */
 1791     ret = sys$crembx(0, &ch2, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
 1792     ERR_SIGNAL(ret);
 1793     dvi_list.item[0].buffer_p = out_mbx_name;
 1794     ret = sys$getdvi(0, ch2, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
 1795     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
 1796     out_mbx_name[64] = '\0';
 1797     out_desc.dsc$a_pointer = out_mbx_name;
 1798     out_desc.dsc$w_length = strlen(out_mbx_name);
 1799     /*
 1800     ** Create GHOSTVIEW and DECW$DISPLAY logical names.
 1801     **
 1802     ** We use CRELNM rather than LIB$SET_LOGICAL because we want these to be
 1803     ** user mode and go away when the program exits. It doesn't matter that we
 1804     ** may set them multiple times, as with the mailbox logicals, since once
 1805     ** Ghostscript starts we don't need them any more.
 1806     */
 1807     sprintf(buf, "%d", XtWindow(w));
 1808     gv_list.item[0].buffer_size = strlen(buf);
 1809     ret = sys$crelnm(0, &lnt_desc, &ghostview_desc, 0, &gv_list);
 1810     ERR_SIGNAL(ret);
 1811     dis_list.item[0].buffer_p = XDisplayString(XtDisplay(w));
 1812     dis_list.item[0].buffer_size = strlen(dis_list.item[0].buffer_p);
 1813     ret = sys$crelnm(0, &lnt_desc, &display_desc, 0, &dis_list);
 1814     ERR_SIGNAL(ret);
 1815     /*
 1816     ** Spawn Ghostscript process
 1817     */
 1818     gvw->ghostview.changed = False;
 1819     gvw->ghostview.busy = True;
 1820     cmd_desc.dsc$w_length = strlen(cmd);
 1821     ret = lib$spawn(&cmd_desc, &in_desc, &out_desc, &nowait, 0, &pid, 0, 0,
 1822     0, 0, 0, 0, 0);
 1823     ERR_SIGNAL(ret);
 1824     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
 1825     /*
 1826     ** Everything worked, initialize IOSBs and save info about interpretter.
 1827     */
 1828     gvw->ghostview.interpreter_pid = pid;
 1829     if (gvw->ghostview.filename == NULL) {
 1830     gvw->ghostview.interpreter_input = ch1;
 1831     gvw->ghostview.interpreter_input_iosb[0] = 0;
 1832     }
 1833     gvw->ghostview.interpreter_output = ch2;
 1834     if (gvw->ghostview.output_buffer == NULL) {
 1835     gvw->ghostview.output_buffer = XtMalloc(GV_BUFSIZ);
 1836     }
 1837     GLInsert(gvw);
 1838     /*
 1839     ** Fake a completed I/O so Output will get called to queue the first I/O.
 1840     */
 1841     gvw->ghostview.interpreter_output_iosb[0] = SS$_NORMAL;
 1842     gvw->ghostview.interpreter_output_iosb[1] = -1;
 1843     IOComplete();
 1844 }
 1845 
 1846 /* Stop the interperter, if present, and remove any Input sources. */
 1847 /* Also reset the busy state. */
 1848 static void
 1849 StopInterpreter(w)
 1850     Widget w;
 1851 {
 1852     int ret;
 1853     
 1854     GhostviewWidget gvw = (GhostviewWidget) w;
 1855     if (gvw->ghostview.interpreter_pid >= 0) {
 1856     ret = sys$delprc(&gvw->ghostview.interpreter_pid, 0);
 1857     if(ret != SS$_NORMAL && ret != SS$_NONEXPR)lib$signal(ret, 0, 0);
 1858     gvw->ghostview.interpreter_pid = -1;
 1859     }
 1860     if (gvw->ghostview.interpreter_input >= 0) {
 1861     (void) sys$dassgn(gvw->ghostview.interpreter_input);
 1862     gvw->ghostview.interpreter_input = -1;
 1863     while (gvw->ghostview.ps_input) {
 1864         struct record_list *ps_old = gvw->ghostview.ps_input;
 1865         gvw->ghostview.ps_input = ps_old->next;
 1866         if (ps_old->close) fclose(ps_old->fp);
 1867         XtFree((char *)ps_old);
 1868     }
 1869     }
 1870     if (gvw->ghostview.interpreter_output >= 0) {
 1871     (void) sys$dassgn(gvw->ghostview.interpreter_output);
 1872     gvw->ghostview.interpreter_output = -1;
 1873     }
 1874     gvw->ghostview.busy = False;
 1875     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
 1876     GLRemove(gvw);
 1877 }
 1878 
 1879 /* GhostviewSendPS:
 1880  *   Queue a portion of a PostScript file for output to ghostscript.
 1881  *   fp: FILE * of the file in question.  NOTE: if you have several
 1882  *   Ghostview widgets reading from the same file.  You must open
 1883  *   a unique FILE * for each widget.
 1884  *   SendPS does not actually send the PostScript, it merely queues it
 1885  *   for output.
 1886  *   begin: position in file (returned from ftell()) to start.
 1887  *   len:   number of bytes to write.
 1888  *
 1889  *   If an interpreter is not running, nothing is queued and
 1890  *   False is returned.
 1891  */
 1892 Boolean
 1893 GhostviewSendPS(w, fp, begin, len, close)
 1894     Widget w;
 1895     FILE *fp;
 1896     long begin;
 1897     unsigned int len;
 1898     Bool close;
 1899 {
 1900     GhostviewWidget gvw = (GhostviewWidget) w;
 1901     struct record_list *ps_new;
 1902 
 1903     if (gvw->ghostview.interpreter_input < 0) return False;
 1904     if(len != 0){
 1905     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
 1906     ps_new->fp = fp;
 1907     ps_new->begin = begin;
 1908     ps_new->len = len;
 1909     ps_new->seek_needed = True;
 1910     ps_new->close = close;
 1911     ps_new->next = NULL;
 1912 
 1913     if (gvw->ghostview.input_buffer == NULL) {
 1914         gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
 1915     }
 1916 
 1917     if (gvw->ghostview.ps_input == NULL) {
 1918         gvw->ghostview.bytes_left = len;
 1919         gvw->ghostview.ps_input = ps_new;
 1920         /*
 1921         ** Fake a completed I/O so Input will get called to queue the
 1922         ** first I/O.
 1923         */
 1924         gvw->ghostview.interpreter_input_iosb[0] = SS$_NORMAL;
 1925         gvw->ghostview.interpreter_input_iosb[1] = -1;
 1926         IOComplete();
 1927     } else {
 1928         struct record_list *p = gvw->ghostview.ps_input;
 1929         while (p->next != NULL) {
 1930         p = p->next;
 1931         }
 1932         p->next = ps_new;
 1933     }
 1934     }
 1935     return True;
 1936 }
 1937 #endif /* VMS */