"Fossies" - the Fresh Open Source Software Archive

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