wdvi

network DVI viewer
Log | Files | Refs

popups.c (40721B)


      1 /*========================================================================*\
      2 
      3 Copyright (c) 2001-2004  Paul Vojta
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining a copy
      6 of this software and associated documentation files (the "Software"), to
      7 deal in the Software without restriction, including without limitation the
      8 rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
      9 sell copies of the Software, and to permit persons to whom the Software is
     10 furnished to do so, subject to the following conditions:
     11 
     12 The above copyright notice and this permission notice shall be included in
     13 all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18 IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF OR CONTRIBUTOR TO
     19 THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     22 IN THE SOFTWARE.
     23 
     24 \*========================================================================*/
     25 
     26 /*
     27  *	Code for popup windows.
     28  */
     29 
     30 #include <sys/socket.h>		/* socketpair */
     31 #include <sys/stat.h>
     32 #include <sys/wait.h>
     33 
     34 #include <ctype.h>
     35 #include <err.h>
     36 #include <errno.h>
     37 #include <fcntl.h>	/* fcntl() */
     38 #include <poll.h>	/* POLLIN */
     39 #include <signal.h>
     40 #include <stdarg.h>
     41 #include <stdlib.h>	/* free(), atoi(), getenv() */
     42 #include <unistd.h>	/* getpid() */
     43 
     44 #include <X11/Xatom.h>
     45 #include <X11/Xos.h>
     46 #include <X11/StringDefs.h>
     47 #include <X11/Shell.h>
     48 #include <X11/Xaw/Form.h>
     49 #include <X11/Xaw/Label.h>
     50 #include <X11/Xaw/Command.h>
     51 #include <X11/Xaw/Toggle.h>
     52 #include <X11/Xaw/AsciiText.h>
     53 #include <X11/Xaw/Viewport.h>
     54 
     55 #include "events.h"	/* handle_messages(), mane, struct xio */
     56 #include "popups.h"
     57 #include "xdvi.h"
     58 #include "util.h"	/* xfopen() */
     59 
     60 
     61 #ifdef EWOULDBLOCK
     62 # ifdef EAGAIN
     63 #  define AGAIN_CONDITION	(errno == EWOULDBLOCK || errno == EAGAIN)
     64 # else
     65 #  define AGAIN_CONDITION	(errno == EWOULDBLOCK)
     66 # endif
     67 #else /* EWOULDBLOCK */
     68 # ifdef EAGAIN
     69 #  define AGAIN_CONDITION	(errno == EAGAIN)
     70 # endif
     71 #endif /* EWOULDBLOCK */
     72 
     73 
     74 
     75 /*
     76  *	New variants on printf.
     77  */
     78 
     79 static int uintlen(i)
     80 	unsigned int i;
     81 {
     82 	int	len = 0;
     83 
     84 	do {
     85 	    ++len;
     86 	    i /= 10;
     87 	}
     88 	while (i != 0);
     89 
     90 	return len;
     91 }
     92 
     93 /*
     94  *	(v)nprintf(fmt, ...) - return the length of the string produced.
     95  */
     96 #define vnprintf(fmt, ap)	vsnprintf(NULL, 0, fmt, ap)
     97 
     98 
     99 /*
    100  *	(v)mprintf(fmt, ...) - printf to allocated string.
    101  */
    102 
    103 static char *
    104 vmprintf(format, args)
    105 	const char	*format;
    106 	va_list		args;
    107 {
    108 	char		*result;
    109 
    110 	if (vasprintf(&result, format, args) < 0)
    111 	    errx(1, "vasprintf failed with format '%s'.", format);
    112 
    113 	return result;
    114 }
    115 
    116 static char *
    117 mprintf(const char *format, ...)
    118 {
    119 	va_list		args;
    120 	char		*result;
    121 
    122 	va_start(args, format);
    123 
    124 	result = vmprintf(format, args);
    125 	va_end(args);
    126 	return result;
    127 }
    128 
    129 
    130 
    131 /*
    132  *	Realize the widget and set the callback for the WM_DESTROY protocol.
    133  */
    134 
    135 static void
    136 XdviXawRealizePopup(shell, callback)
    137 	Widget		shell;
    138 	XtCallbackProc	callback;
    139 {
    140 	XtRealizeWidget(shell);
    141 
    142 	XSetWMProtocols(DISP, XtWindow(shell), &XA_WM_DELETE_WINDOW, 1);
    143 	XtAddEventHandler(shell, NoEventMask, True, handle_messages, callback);
    144 }
    145 
    146 
    147 /*
    148  *	Warning popup - used for most error and notice conditions.
    149  *	It includes a message and a button (and, in the case of the Motif
    150  *	toolkit, a bitmap).
    151  */
    152 
    153 static	void
    154 warn_callback(w, client_data, call_data)
    155 	Widget		w;
    156 	XtPointer	client_data;
    157 	XtPointer	call_data;
    158 {
    159 	/* XtIsShell(w) holds if this is caused by the WM_DESTROY protocol */
    160 	XtDestroyWidget(XtIsShell(w) ? w : (Widget) client_data);
    161 }
    162 
    163 
    164 void
    165 do_popup(shell)
    166 	Widget		shell;
    167 {
    168 	Position	x, y;
    169 	Dimension	w1, h1, w2, h2;
    170 
    171 	/* Get the size and location of the main window */
    172 	x = y = 0;
    173 	w1 = WidthOfScreen(SCRN);
    174 	h1 = HeightOfScreen(SCRN);
    175 	if (mane.win != (Window) 0)
    176 	    XtVaGetValues(top_level, XtNx, &x, XtNy, &y,
    177 	      XtNwidth, &w1, XtNheight, &h1, NULL);
    178 
    179 	/* Get the size of the popup window */
    180 	XtVaGetValues(shell, XtNwidth, &w2, XtNheight, &h2, NULL);
    181 
    182 	/* Center the popup over the main window */
    183 	XtVaSetValues(shell, XtNx, x + (w1 - w2) / 2,
    184 	  XtNy, y + (h1 - h2) / 2, NULL);
    185 
    186 	XtPopup(shell, XtGrabNone);
    187 }
    188 
    189 Widget
    190 warning_popup(message, button_name, callback)
    191 	const char	*message;
    192 	const char	*button_name;
    193 	XtCallbackProc	callback;
    194 {
    195 	Widget		shell, form, label, button;
    196 	Dimension	w1, w2, bw;
    197 	int		dist;
    198 
    199 	XtAccelerators accels_cr_click = XtParseAcceleratorTable(
    200 		"<Key>Return:set()notify()unset()\n"
    201 		"<Btn1Down>:set()\n"
    202 		"<Btn1Up>:notify()unset()");
    203 
    204 	shell = XtVaCreatePopupShell("warn", transientShellWidgetClass,
    205 	  top_level,
    206 	  XtNtitle, "Xdvi warning",
    207 	  XtNmappedWhenManaged, False,
    208 	  XtNtransientFor, top_level,
    209 	  NULL);
    210 	form = XtCreateManagedWidget("form", formWidgetClass, shell,
    211 	  NULL, 0);
    212 	label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
    213 	  XtNlabel, message,
    214 	  XtNborderWidth, 0,
    215 	  XtNleft, XtChainLeft,
    216 	  XtNright, XtChainLeft,
    217 	  NULL);
    218 	button = XtVaCreateManagedWidget(button_name, commandWidgetClass, form,
    219 	  XtNaccelerators, accels_cr_click,
    220 	  XtNfromVert, label,
    221 	  NULL);
    222 	XtAddCallback(button, XtNcallback,
    223 	  callback != NULL ? callback : warn_callback, shell);
    224 	XtInstallAccelerators(form, button);
    225 
    226 	/* Center button under message */
    227 	XtVaGetValues(label, XtNwidth, &w1, NULL);
    228 	XtVaGetValues(button, XtNwidth, &w2, XtNborderWidth, &bw, NULL);
    229 	w2 += 2 * bw;
    230 	if (w1 > w2) {
    231 	    XtVaGetValues(button, XtNhorizDistance, &dist, NULL);
    232 	    XtVaSetValues(button, XtNhorizDistance, dist + (w1 - w2) / 2, NULL);
    233 	}
    234 	else if (w1 < w2) {
    235 	    XtVaGetValues(label, XtNhorizDistance, &dist, NULL);
    236 	    XtVaSetValues(label, XtNhorizDistance, dist + (w2 - w1) / 2, NULL);
    237 	}
    238 
    239 	XdviXawRealizePopup(shell, callback != NULL ? callback : warn_callback);
    240 	do_popup(shell);
    241 
    242 	return shell;
    243 }
    244 
    245 
    246 Widget
    247 warning_popup_long(const char *format,
    248   const char *button_name, XtCallbackProc callback, ...)
    249 {
    250 	va_list		args;
    251 	char		*str;
    252 	Widget		w;
    253 
    254 	va_start(args, callback);
    255 	str = vmprintf(format, args);
    256 	va_end(args);
    257 
    258 	w = warning_popup(str, button_name, callback);
    259 
    260 	free(str);
    261 
    262 	return w;
    263 }
    264 
    265 
    266 
    267 /*
    268  *	Confirm popup - Pop up a window and ask a yes/no question.
    269  */
    270 
    271 static Widget
    272 confirm_popup(message, button_name, callback, yes_answer, no_answer)
    273 	const char	*message;
    274 	const char	*button_name;
    275 	XtCallbackProc	callback;
    276 	XtPointer	yes_answer, no_answer;
    277 {
    278 	XtAccelerators	accels_cr;
    279 	Widget		shell, form, label, button1, button2;
    280 	int		ddist;
    281 	Dimension	w0, w1, w2, bw1, bw2, tw;
    282 
    283 	accels_cr = XtParseAcceleratorTable("<Key>Return:set()notify()unset()");
    284 
    285 	shell = XtVaCreatePopupShell("confirm", transientShellWidgetClass,
    286 	  top_level,
    287 	  XtNtitle, "Xdvi question",
    288 	  XtNmappedWhenManaged, False,
    289 	  XtNtransientFor, top_level,
    290 	  NULL);
    291 	form = XtCreateManagedWidget("form", formWidgetClass, shell,
    292 	  NULL, 0);
    293 	label = XtVaCreateManagedWidget("label", labelWidgetClass, form,
    294 	  XtNlabel, message,
    295 	  XtNborderWidth, 0,
    296 	  XtNleft, XtChainLeft,
    297 	  XtNright, XtChainLeft,
    298 	  NULL);
    299 
    300 	button1 = XtVaCreateManagedWidget(button_name, commandWidgetClass, form,
    301 	  XtNaccelerators, accels_cr,
    302 	  XtNfromVert, label,
    303 	  NULL);
    304 	XtAddCallback(button1, XtNcallback, callback, yes_answer);
    305 	XtInstallAccelerators(form, button1);
    306 
    307 	button2 = XtVaCreateManagedWidget("Cancel", commandWidgetClass, form,
    308 	  XtNfromVert, label,
    309 	  XtNfromHoriz, button1,
    310 	  NULL);
    311 	XtAddCallback(button2, XtNcallback, callback, no_answer);
    312 
    313 	/* Move cancel button to the right */
    314 	XtVaGetValues(label, XtNwidth, &w0, NULL);
    315 	XtVaGetValues(button1, XtNwidth, &w1, XtNborderWidth, &bw1, NULL);
    316 	XtVaGetValues(button2, XtNwidth, &w2, XtNborderWidth, &bw2, NULL);
    317 	XtVaGetValues(form, XtNdefaultDistance, &ddist, NULL);
    318 	tw = w1 + w2 + 2 * (bw1 + bw2);
    319 	if (tw < w0 + ddist)
    320 	    XtVaSetValues(button2, XtNhorizDistance, w0 - tw, NULL);
    321 
    322 	XdviXawRealizePopup(shell, callback);
    323 	do_popup(shell);
    324 
    325 	return shell;
    326 }
    327 
    328 /*
    329  *	Print popup - pop up a window to allow the user to print page(s)
    330  *	from the dvi file.
    331  */
    332 
    333 static	Boolean	print_active	= False;	/* if print window is showing */
    334 static	Widget	print_shell	= NULL;		/* shell widget of popup */
    335 static	Widget	r1radio1, r1radio2;
    336 static	Widget	r2label, r2text;
    337 static	Widget	r3label, r3text;
    338 static	Widget	r4text;
    339 static	Widget	r6radio, r7radio;
    340 static	Widget	r7text1, r7text2;
    341 static	Widget	r8text;
    342 static	Widget	r9prev;
    343 
    344 /* &r1radio1 = printer, &r1radio2 = to file */
    345 static	Widget	*pr_current_fileradio	= &r1radio1;
    346 
    347 /* &r6radio = whole file, &r7radio = page range */
    348 static	Widget	*pr_current_pageradio	= &r6radio;
    349 
    350 /* Stuff to save between popups */
    351 
    352 static	Widget		*pr_save_fileradio = &r1radio1;
    353 static	Widget		*pr_save_pageradio = &r6radio;
    354 static	const char	*pr_save_cmd	= NULL;
    355 static	const char	*pr_save_file	= NULL;
    356 static	const char	*pr_save_printer= NULL;
    357 static	const char	*pr_save_xargs	= NULL;
    358 
    359 /* Things to do with running the dvips process */
    360 
    361 static	Boolean	printlog_active	= False;	/* if the print log is active */
    362 static	Widget	printlog_shell	= NULL;
    363 static	Widget	printlog_keep;
    364 
    365 /* Forward reference */
    366 
    367 static	void	print_precheck(void);
    368 
    369 
    370 static void
    371 set_sensitivity(fileradio)
    372 	Widget	*fileradio;
    373 {
    374 	Boolean	sensitivity;
    375 
    376 	if (fileradio == pr_current_fileradio) /* if no change */
    377 	    return;
    378 
    379 	pr_current_fileradio = fileradio;
    380 
    381 	sensitivity = (fileradio == &r1radio1);
    382 	XtSetSensitive(r2label, sensitivity);
    383 	XtSetSensitive(r2text, sensitivity);
    384 
    385 	sensitivity ^= (True ^ False);
    386 	XtSetSensitive(r3label, sensitivity);
    387 	XtSetSensitive(r3text, sensitivity);
    388 }
    389 
    390 static	void
    391 cb_print_cancel(w, client_data, call_data)
    392 	Widget		w;
    393 	XtPointer	client_data;
    394 	XtPointer	call_data;
    395 {
    396 	XtPopdown(print_shell);
    397 	print_active = False;
    398 
    399 	if (w == r9prev && !printlog_active) {
    400 	    XtSetSensitive(printlog_keep, False);
    401 	    XtPopup(printlog_shell, XtGrabNone);
    402 	    printlog_active = True;
    403 	}
    404 }
    405 
    406 static	void
    407 cb_print(w, client_data, call_data)
    408 	Widget		w;
    409 	XtPointer	client_data;
    410 	XtPointer	call_data;
    411 {
    412 	print_precheck();
    413 }
    414 
    415 static void print_act_go(Widget, XEvent *, String *, Cardinal *);
    416 static void printlog_act_close(Widget, XEvent *, String *, Cardinal *);
    417 static void printlog_act_keep(Widget, XEvent *, String *, Cardinal *);
    418 static void printlog_act_unkeep(Widget, XEvent *, String *, Cardinal *);
    419 static void printlog_act_cancel(Widget, XEvent *, String *, Cardinal *);
    420 
    421 static	XtActionsRec	print_actions[]	= {
    422 	{"printInternal",	print_act_go},
    423 	{"printlogIntClose",	printlog_act_close},
    424 	{"printlogIntKeep",	printlog_act_keep},
    425 	{"printlogIntUnkeep",	printlog_act_unkeep},
    426 	{"printlogIntCancel",	printlog_act_cancel},
    427 };
    428 
    429 
    430 #ifndef PRINTINDENT
    431 # define PRINTINDENT	25
    432 #endif
    433 
    434 static	void
    435 cb_print_vs_file(w, client_data, call_data)
    436 	Widget		w;
    437 	XtPointer	client_data;
    438 	XtPointer	call_data;
    439 {
    440 	Widget	*fileradio	= XawToggleGetCurrent(r1radio1);
    441 
    442 	if (fileradio != NULL)
    443 	    set_sensitivity(fileradio);
    444 }
    445 
    446 static	void
    447 cb_range(w, client_data, call_data)
    448 	Widget		w;
    449 	XtPointer	client_data;
    450 	XtPointer	call_data;
    451 {
    452 	Widget	*pageradio	= XawToggleGetCurrent(r6radio);
    453 
    454 	if (pageradio != NULL)
    455 	    pr_current_pageradio = pageradio;
    456 }
    457 
    458 static void
    459 range_handle_key(widget, closure, ev, cont)
    460 	Widget		widget;
    461 	XtPointer	closure;
    462 	XEvent		*ev;
    463 	Boolean		*cont;
    464 {
    465 	if (pr_current_pageradio != &r7radio)
    466 	    XawToggleSetCurrent(r6radio, &r7radio);
    467 }
    468 
    469 static	void
    470 print_act_go(Widget w, XEvent *event, String *params, Cardinal *num_params)
    471 {
    472 	print_precheck();
    473 }
    474 
    475 void
    476 Act_print(Widget w, XEvent *event, String *params, Cardinal *num_params)
    477 {
    478 	static	Widget	r7label3;
    479 	static	Widget	r9cancel;
    480 	static	Dimension sw;	/* shell width */
    481 	static	int	canceladjust;
    482 	char		*ofstring;
    483 
    484 	if (print_active) {
    485 	    XRaiseWindow(DISP, XtWindow(print_shell));
    486 	    return;
    487 	}
    488 
    489 	if (printlog_active) {
    490 	    XRaiseWindow(DISP, XtWindow(printlog_shell));
    491 	    return;
    492 	}
    493 
    494 	if (dvi_file == NULL) {
    495 	    WARN(XmDIALOG_ERROR, "No active dvi file");
    496 	    return;
    497 	}
    498 
    499 	ofstring = (pageno_correct == 1 ? mprintf("of %d", total_pages)
    500 	  : mprintf("of %d to %d", pageno_correct,
    501 	    total_pages + pageno_correct - 1));
    502 
    503 	if (print_shell == NULL) {
    504 	    Widget		form;
    505 	    Widget		r1label;
    506 	    Widget		r4label;
    507 	    Widget		r5label;
    508 	    Widget		r6form;
    509 	    Widget		r7form, r7label1, r7label2;
    510 	    Widget		r8label;
    511 	    Widget		r9ok;
    512 	    XtTranslations	xlats, xlats2;
    513 	    XtAccelerators	accels2;
    514 	    int			ddist;
    515 
    516 	    XtAddActions(print_actions, XtNumber(print_actions));
    517 	    print_shell = XtVaCreatePopupShell("print",
    518 	      transientShellWidgetClass,
    519 	      top_level,
    520 	      XtNtitle, "Print dvi file",
    521 	      XtNmappedWhenManaged, False,
    522 	      XtNtransientFor, top_level,
    523 	      XtNallowShellResize, True,
    524 	      NULL);
    525 
    526 	    form = XtCreateManagedWidget("form", formWidgetClass, print_shell,
    527 	      NULL, 0);
    528 	    XtVaGetValues(form, XtNdefaultDistance, &ddist, NULL);
    529 
    530 	    r1label = XtVaCreateManagedWidget("printto", labelWidgetClass, form,
    531 	      XtNlabel, "Print to:",
    532 	      XtNborderWidth, 0,
    533 	      XtNleft, XawChainLeft,
    534 	      XtNright, XawChainLeft,
    535 	      NULL);
    536 
    537 	    xlats = XtParseTranslationTable("<EnterWindow>:highlight(Always)\n\
    538 <LeaveWindow>:unhighlight()\n\
    539 <Btn1Down>,<Btn1Up>:set()notify()");
    540 	    r1radio1 = XtVaCreateManagedWidget("toprinter", toggleWidgetClass,
    541 	      form,
    542 	      XtNlabel, "Printer",
    543 	      XtNradioData, &r1radio1,
    544 	      XtNstate, True,
    545 	      XtNtranslations, xlats,
    546 	      XtNfromHoriz, r1label,
    547 	      XtNleft, XawChainLeft,
    548 	      XtNright, XawChainLeft,
    549 	      NULL);
    550 	    XtAddCallback(r1radio1, XtNcallback, cb_print_vs_file, NULL);
    551 
    552 	    r1radio2 = XtVaCreateManagedWidget("tofile", toggleWidgetClass,
    553 	      form,
    554 	      XtNlabel, "File",
    555 	      XtNradioGroup, r1radio1,
    556 	      XtNradioData, &r1radio2,
    557 	      XtNtranslations, xlats,
    558 	      XtNfromHoriz, r1radio1,
    559 	      XtNleft, XawChainLeft,
    560 	      XtNright, XawChainLeft,
    561 	      NULL);
    562 	    XtAddCallback(r1radio2, XtNcallback, cb_print_vs_file, NULL);
    563 
    564 	    /* Rows 2, 3:  Print command or file name */
    565 
    566 	    r2label = XtVaCreateManagedWidget("prncommand", labelWidgetClass,
    567 	      form,
    568 	      XtNlabel, "Print command:",
    569 	      XtNborderWidth, 0,
    570 	      XtNfromVert, r1radio1,
    571 	      XtNhorizDistance, ddist + PRINTINDENT,
    572 	      XtNleft, XawChainLeft,
    573 	      XtNright, XawChainLeft,
    574 	      NULL);
    575 
    576 	    r2text = XtVaCreateManagedWidget("prntext", asciiTextWidgetClass,
    577 	      form,
    578 	      XtNdataCompression, False,
    579 	      XtNeditType, XawtextEdit,
    580 	      XtNfromHoriz, r2label,
    581 	      XtNfromVert, r1radio1,
    582 	      XtNleft, XawChainLeft,
    583 	      XtNright, XawChainLeft,
    584 	      NULL);
    585 	    xlats2 = XtParseTranslationTable("<Key>Return:printInternal()");
    586 	    XtOverrideTranslations(r2text, xlats2);
    587 
    588 	    r3label = XtVaCreateManagedWidget("filename", labelWidgetClass,
    589 	      form,
    590 	      XtNlabel, "File name:",
    591 	      XtNsensitive, False,
    592 	      XtNborderWidth, 0,
    593 	      XtNfromVert, r2text,
    594 	      XtNhorizDistance, ddist + PRINTINDENT,
    595 	      XtNleft, XawChainLeft,
    596 	      XtNright, XawChainLeft,
    597 	      NULL);
    598 
    599 	    r3text = XtVaCreateManagedWidget("filetext", asciiTextWidgetClass,
    600 	      form,
    601 	      XtNdataCompression, False,
    602 	      XtNeditType, XawtextEdit,
    603 	      XtNsensitive, False,
    604 	      XtNfromHoriz, r3label,
    605 	      XtNfromVert, r2text,
    606 	      XtNleft, XawChainLeft,
    607 	      XtNright, XawChainLeft,
    608 	      NULL);
    609 	    XtOverrideTranslations(r3text, xlats2);
    610 
    611 	    {	/* align left edges of asciitext widgets */
    612 		Dimension w2, w3;
    613 
    614 		XtVaGetValues(r2label, XtNwidth, &w2, NULL);
    615 		XtVaGetValues(r3label, XtNwidth, &w3, NULL);
    616 		if (w3 > w2)
    617 		    XtVaSetValues(r2text, XtNhorizDistance, ddist + (w3 - w2),
    618 		      NULL);
    619 		else if (w2 > w3)
    620 		    XtVaSetValues(r3text, XtNhorizDistance, ddist + (w2 - w3),
    621 		      NULL);
    622 	    }
    623 
    624 	    /* Row 4:  printer name (for dvips) */
    625 
    626 	    r4label = XtVaCreateManagedWidget("prname", labelWidgetClass,
    627 	      form,
    628 	      XtNlabel, "Printer name (used by dvips):",
    629 	      XtNborderWidth, 0,
    630 	      XtNfromVert, r3text,
    631 	      XtNleft, XawChainLeft,
    632 	      XtNright, XawChainLeft,
    633 	      NULL);
    634 
    635 	    r4text = XtVaCreateManagedWidget("prtext", asciiTextWidgetClass,
    636 	      form,
    637 	      XtNdataCompression, False,
    638 	      XtNeditType, XawtextEdit,
    639 	      XtNwidth, 50,
    640 	      XtNfromHoriz, r4label,
    641 	      XtNfromVert, r3text,
    642 	      XtNleft, XawChainLeft,
    643 	      XtNright, XawChainLeft,
    644 	      NULL);
    645 	    XtOverrideTranslations(r4text, xlats2);
    646 
    647 	    /* Rows 5-7:  page selection */
    648 
    649 	    r5label = XtVaCreateManagedWidget("rangelab", labelWidgetClass,
    650 	      form,
    651 	      XtNlabel, "Print range:",
    652 	      XtNborderWidth, 0,
    653 	      XtNfromVert, r4text,
    654 	      XtNleft, XawChainLeft,
    655 	      XtNright, XawChainLeft,
    656 	      NULL);
    657 
    658 	    r6form = XtVaCreateManagedWidget("rangeallform", formWidgetClass,
    659 	      form,
    660 	      XtNdefaultDistance, 0,
    661 	      XtNborderWidth, 0,
    662 	      XtNfromVert, r5label,
    663 	      XtNhorizDistance, ddist + PRINTINDENT,
    664 	      XtNleft, XawChainLeft,
    665 	      XtNright, XawChainLeft,
    666 	      NULL);
    667 
    668 	    accels2 = XtParseAcceleratorTable(
    669 	      "<Btn1Down>,<Btn1Up>:set()notify()");
    670 	    r6radio = XtVaCreateManagedWidget("rangeall", toggleWidgetClass,
    671 	      r6form,
    672 	      XtNlabel, " ",
    673 	      XtNradioData, &r6radio,
    674 	      XtNstate, True,
    675 	      XtNtranslations, xlats,
    676 	      XtNaccelerators, accels2,
    677 	      NULL);
    678 	    XtAddCallback(r6radio, XtNcallback, cb_range, NULL);
    679 	    XtInstallAccelerators(r6form, r6radio);
    680 
    681 	    (void) XtVaCreateManagedWidget("rangealllab", labelWidgetClass,
    682 	      r6form,
    683 	      XtNlabel, "All",
    684 	      XtNborderWidth, 0,
    685 	      XtNfromHoriz, r6radio,
    686 	      XtNhorizDistance, ddist,
    687 	      NULL);
    688 
    689 	    r7form = XtVaCreateManagedWidget("rangefromtoform", formWidgetClass,
    690 	      form,
    691 	      XtNdefaultDistance, 0,
    692 	      XtNborderWidth, 0,
    693 	      XtNresizable, True,
    694 	      XtNfromVert, r6form,
    695 	      XtNhorizDistance, ddist + PRINTINDENT,
    696 	      XtNleft, XawChainLeft,
    697 	      XtNright, XawChainLeft,
    698 	      NULL);
    699 
    700 	    r7radio = XtVaCreateManagedWidget("rangefromto", toggleWidgetClass,
    701 	      r7form,
    702 	      XtNlabel, " ",
    703 	      XtNradioGroup, r6radio,
    704 	      XtNradioData, &r7radio,
    705 	      XtNtranslations, xlats,
    706 	      XtNaccelerators, accels2,
    707 	      NULL);
    708 	    XtAddCallback(r7radio, XtNcallback, cb_range, NULL);
    709 	    XtInstallAccelerators(r7form, r7radio);
    710 
    711 	    r7label1 = XtVaCreateManagedWidget("rangefromlab", labelWidgetClass,
    712 	      r7form,
    713 	      XtNlabel, "From",
    714 	      XtNborderWidth, 0,
    715 	      XtNfromHoriz, r7radio,
    716 	      XtNhorizDistance, ddist,
    717 	      NULL);
    718 
    719 	    r7text1 = XtVaCreateManagedWidget("rangefrom", asciiTextWidgetClass,
    720 	      r7form,
    721 	      XtNdataCompression, False,
    722 	      XtNeditType, XawtextEdit,
    723 	      XtNwidth, 50,
    724 	      XtNfromHoriz, r7label1,
    725 	      XtNhorizDistance, ddist,
    726 	      NULL);
    727 	    XtOverrideTranslations(r7text1, xlats2);
    728 	    XtAddEventHandler(r7text1, KeyPressMask, False,
    729 	      range_handle_key, (XtPointer) NULL);
    730 
    731 	    r7label2 = XtVaCreateManagedWidget("rangetolab", labelWidgetClass,
    732 	      r7form,
    733 	      XtNlabel, "to",
    734 	      XtNborderWidth, 0,
    735 	      XtNfromHoriz, r7text1,
    736 	      XtNhorizDistance, ddist,
    737 	      NULL);
    738 
    739 	    r7text2 = XtVaCreateManagedWidget("rangeto", asciiTextWidgetClass,
    740 	      r7form,
    741 	      XtNdataCompression, False,
    742 	      XtNeditType, XawtextEdit,
    743 	      XtNwidth, 50,
    744 	      XtNfromHoriz, r7label2,
    745 	      XtNhorizDistance, ddist,
    746 	      NULL);
    747 	    XtOverrideTranslations(r7text2, xlats2);
    748 	    XtAddEventHandler(r7text2, KeyPressMask, False,
    749 	      range_handle_key, (XtPointer) NULL);
    750 
    751 	    r7label3 = XtVaCreateManagedWidget("rangeof", labelWidgetClass,
    752 	      r7form,
    753 	      XtNlabel, ofstring,
    754 	      XtNborderWidth, 0,
    755 	      XtNresizable, True,
    756 	      XtNfromHoriz, r7text2,
    757 	      XtNhorizDistance, ddist,
    758 	      NULL);
    759 
    760 	    /* Row 8:  additional dvips args */
    761 
    762 	    r8label = XtVaCreateManagedWidget("xargsname", labelWidgetClass,
    763 	      form,
    764 	      XtNlabel, "Additional dvips arguments (optional):",
    765 	      XtNborderWidth, 0,
    766 	      XtNfromVert, r7form,
    767 	      XtNleft, XawChainLeft,
    768 	      XtNright, XawChainLeft,
    769 	      NULL);
    770 
    771 	    r8text = XtVaCreateManagedWidget("xargstext", asciiTextWidgetClass,
    772 	      form,
    773 	      XtNdataCompression, False,
    774 	      XtNeditType, XawtextEdit,
    775 	      XtNwidth, 50,
    776 	      XtNresizable, True,
    777 	      XtNfromVert, r8label,
    778 	      XtNhorizDistance, ddist + PRINTINDENT,
    779 	      XtNleft, XawChainLeft,
    780 	      XtNright, XawChainRight,
    781 	      NULL);
    782 	    XtOverrideTranslations(r8text, xlats2);
    783 
    784 	    /* Row 9:  action buttons */
    785 
    786 	XtAccelerators accels_cr;
    787 	accels_cr = XtParseAcceleratorTable("<Key>Return:set()notify()unset()");
    788 
    789 	    r9ok = XtVaCreateManagedWidget("ok", commandWidgetClass, form,
    790 	      XtNlabel, "OK",
    791 	      XtNaccelerators, accels_cr,
    792 	      XtNfromVert, r8text,
    793 	      XtNleft, XawChainLeft,
    794 	      XtNright, XawChainLeft,
    795 	      NULL);
    796 	    XtAddCallback(r9ok, XtNcallback, cb_print, NULL);
    797 	    XtInstallAccelerators(form, r9ok);
    798 	    XtInstallAccelerators(r2text, r9ok);
    799 	    XtInstallAccelerators(r3text, r9ok);
    800 
    801 	    r9prev = XtVaCreateManagedWidget("prev", commandWidgetClass, form,
    802 	      XtNlabel, "Show previous log",
    803 	      XtNsensitive, False,
    804 	      XtNfromHoriz, r9ok,
    805 	      XtNfromVert, r8text,
    806 	      XtNleft, XawChainLeft,
    807 	      XtNright, XawChainLeft,
    808 	      NULL);
    809 	    XtAddCallback(r9prev, XtNcallback, cb_print_cancel, NULL);
    810 
    811 	    r9cancel = XtVaCreateManagedWidget("cancel", commandWidgetClass,
    812 	      form,
    813 	      XtNlabel, "Cancel",
    814 	      XtNfromHoriz, r9prev,
    815 	      XtNfromVert, r8text,
    816 	      XtNleft, XawChainRight,
    817 	      XtNright, XawChainRight,
    818 	      NULL);
    819 	    XtAddCallback(r9cancel, XtNcallback, cb_print_cancel, NULL);
    820 
    821 	    /* Realize and set destroy callback */
    822 	    XdviXawRealizePopup(print_shell, cb_print_cancel);
    823 
    824 	    {	/* Center the popup over the main window */
    825 		Dimension	sh;	/* sw was defined earlier */
    826 		Position	tx, ty;
    827 		Dimension	tw, th;
    828 
    829 		/* Get the size of the popup window */
    830 		XtVaGetValues(print_shell, XtNwidth, &sw, XtNheight, &sh, NULL);
    831 
    832 		/* Center the popup over the main window */
    833 		XtVaGetValues(top_level, XtNx, &tx, XtNy, &ty,
    834 		  XtNwidth, &tw, XtNheight, &th, NULL);
    835 		XtVaSetValues(print_shell, XtNx, tx + (tw - sw) / 2,
    836 		  XtNy, ty + (th - sh) / 2, NULL);
    837 	    }
    838 
    839 	    {
    840 		/* Set width of dvips-xargs text widget */
    841 		Dimension	b;
    842 
    843 		XtVaGetValues(r8text, XtNborderWidth, &b, NULL);
    844 		XtVaSetValues(r8text,
    845 		  XtNwidth, sw - 2 * (b + ddist) - PRINTINDENT,
    846 		  NULL);
    847 	    }
    848 
    849 	    {	/* Set canceladjust */
    850 		Dimension	cw, cb;
    851 		Position	cx;
    852 
    853 		XtVaGetValues(r9cancel, XtNwidth, &cw, XtNborderWidth, &cb,
    854 		  XtNx, &cx, NULL);
    855 		canceladjust = cx + cw + 2 * cb - ddist;
    856 	    }
    857 	}
    858 	else {	/* if the window was already created */
    859 	    XawToggleSetCurrent(r1radio1, pr_save_fileradio);
    860 	    set_sensitivity(pr_save_fileradio);
    861 	    XawToggleSetCurrent(r6radio, pr_save_pageradio);
    862 
    863 	    XtVaSetValues(r7label3, XtNlabel, ofstring, NULL);
    864 
    865 	    if (pr_save_printer != NULL) {
    866 		XtVaSetValues(r4text, XtNstring, pr_save_printer, NULL);
    867 		XawTextSetInsertionPoint(r4text, strlen(pr_save_printer));
    868 	    }
    869 	    else
    870 		XtVaSetValues(r4text, XtNstring, "", NULL);
    871 
    872 	    if (pr_save_xargs != NULL) {
    873 		XtVaSetValues(r8text, XtNstring, pr_save_xargs, NULL);
    874 		XawTextSetInsertionPoint(r8text, strlen(pr_save_xargs));
    875 	    }
    876 	    else
    877 		XtVaSetValues(r8text, XtNstring, "", NULL);
    878 
    879 	    XtVaGetValues(print_shell, XtNwidth, &sw, NULL);
    880 	}
    881 
    882 	free(ofstring);
    883 	XtVaSetValues(r9prev, XtNhorizDistance, (sw - canceladjust) / 2, NULL);
    884 	XtVaSetValues(r9cancel, XtNhorizDistance, (sw - canceladjust) / 2,
    885 	  NULL);
    886 
    887 	{	/* set initial values of print command and file name fields */
    888 	    const char *s;
    889 	    char s2[3 * sizeof(int) + 1];
    890 
    891 	    s = pr_save_cmd;
    892 	    if (s == NULL) s = "lpr";
    893 	    /* for some reason two separate XtVaSetValues calls are necessary */
    894 	    XtVaSetValues(r2text, XtNstring, s, NULL);
    895 	    XawTextSetInsertionPoint(r2text, strlen(s));
    896 
    897 	    if (pr_save_file == NULL) {
    898 		char		*s2;
    899 		int		n;
    900 
    901 		s = rindex(dvi_name, '/');
    902 		if (s == NULL) s = dvi_name;
    903 		else ++s;
    904 		n = strlen(s);
    905 		if (n >= 4 && strncasecmp(s + n - 4, ".dvi", 4) == 0) n -= 4;
    906 		s2 = xmalloc(n + 4);
    907 		memcpy(s2, s, n);
    908 		memcpy(s2 + n, ".ps", 4);
    909 		pr_save_file = s2;
    910 	    }
    911 	    s = pr_save_file;
    912 	    XtVaSetValues(r3text, XtNstring, s, NULL);
    913 	    XawTextSetInsertionPoint(r3text, strlen(s));
    914 
    915 	    (void) snprintf(s2, sizeof s2, "%d", current_page + pageno_correct);
    916 	    XtVaSetValues(r7text1, XtNstring, s2, NULL);
    917 	    XawTextSetInsertionPoint(r7text1, strlen(s2));
    918 	    XtVaSetValues(r7text2, XtNstring, s2, NULL);
    919 	    XawTextSetInsertionPoint(r7text2, strlen(s2));
    920 	}
    921 
    922 	XtPopup(print_shell, XtGrabNone);
    923 	print_active = True;
    924 }
    925 
    926 
    927 
    928 /*
    929  *	The actual printing
    930  */
    931 
    932 static	int	pageno1, pageno2;		/* page range */
    933 static	Widget	print_confirm = NULL;
    934 static	Widget	printlog_text;
    935 static	Widget	printlog_close;
    936 static	Widget	printlog_cancel;
    937 
    938 static	XawTextPosition	printlog_length;
    939 
    940 static	void		dvips_ended(int);
    941 static	struct xchild	print_child	= {NULL, 0, True, dvips_ended};
    942 
    943 static	void		read_from_dvips(void);
    944 static	struct xio	print_xio	= {NULL, 0, POLLIN, NULL,
    945 					   read_from_dvips, NULL};
    946 
    947 static	int		dvips_status;
    948 #define	DVIPS_STAT_NONE	0
    949 #define	DVIPS_STAT_RUN	1
    950 #define	DVIPS_STAT_WAIT	2
    951 static	int		dvips_sig;	/* SIGINT or SIGKILL */
    952 
    953 /* Forward reference */
    954 
    955 static void print_do_it(void);
    956 
    957 static int
    958 getpageno(w)
    959 	Widget	w;
    960 {
    961 	char	*s, *p;
    962 
    963 	XtVaGetValues(w, XtNstring, &s, NULL);
    964 	p = s;
    965 	if (*p == '-') ++p;
    966 	if (!isdigit(*p)) return 0;
    967 	do ++p; while (isdigit(*p));
    968 	if (*p != '\0') return 0;
    969 
    970 	return atoi(s) - pageno_correct + 1;
    971 }
    972 
    973 static	void
    974 cb_print_confirm(w, client_data, call_data)
    975 	Widget		w;
    976 	XtPointer	client_data;
    977 	XtPointer	call_data;
    978 {
    979 	XtDestroyWidget(print_confirm);
    980 	print_confirm = NULL;
    981 
    982 	if (client_data != NULL)
    983 	    print_do_it();
    984 }
    985 
    986 static	void
    987 print_precheck()
    988 {
    989 	char		*dest;
    990 	struct stat	statbuf;
    991 
    992 	/* Check for active confirm box */
    993 	if (print_confirm != NULL) {
    994 	    XRaiseWindow(DISP, XtWindow(print_confirm));
    995 	    return;
    996 	}
    997 
    998 	/* Validate page range (if given) */
    999 	if (pr_current_pageradio == &r7radio) {	/* if page range selected */
   1000 	    pageno1 = getpageno(r7text1);
   1001 	    pageno2 = getpageno(r7text2);
   1002 	    if (pageno1 <= 0 || pageno1 > pageno2 || pageno2 > total_pages) {
   1003 		WARN(XmDIALOG_ERROR, "Invalid page range");
   1004 		return;
   1005 	    }
   1006 	}
   1007 
   1008 	/* Pop down window */
   1009 	cb_print_cancel(NULL, NULL, NULL);
   1010 
   1011 	/* Check for whether to clobber existing file */
   1012 	if (pr_current_fileradio == &r1radio2) {	/* if print to file */
   1013 	    XtVaGetValues(r3text, XtNstring, &dest, NULL);
   1014 	    if (stat(dest, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) {
   1015 		char	*msg;
   1016 
   1017 		msg = mprintf("Overwrite existing file %s?", dest);
   1018 		print_confirm = confirm_popup(msg, "OK", cb_print_confirm,
   1019 		  (XtPointer) print_shell, NULL);
   1020 		free(msg);
   1021 		return;
   1022 	    }
   1023 	}
   1024 
   1025 	print_do_it();
   1026 }
   1027 
   1028 
   1029 static	void	cb_dvips_close(Widget, XtPointer, XtPointer);
   1030 static	void	cb_dvips_keep(Widget, XtPointer, XtPointer);
   1031 static	void	cb_dvips_cancel(Widget, XtPointer, XtPointer);
   1032 static	void	cb_dvips_delete(Widget, XtPointer, XtPointer);
   1033 static	void	printlog_append(const char *, int);
   1034 
   1035 struct dvips_env {
   1036 	const char	*envname;
   1037 	const char	*en1, *en2;
   1038 	const char	*envval;
   1039 };
   1040 
   1041 static	struct dvips_env	dvips_envs[] = {
   1042 	{NULL, "TEXPICTS", "TEXINPUTS", NULL},
   1043 	{NULL, "TEXPSHEADERS", "PSHEADERS", NULL}};
   1044 
   1045 static	void
   1046 print_do_it()
   1047 {
   1048 	const char	*dest;
   1049 	const char	*prn;
   1050 	const char	*xargs;
   1051 	FILE		*f;
   1052 	unsigned int	len;	/* length of command line */
   1053 	unsigned int	argc;
   1054 	char		**argv;
   1055 	char		**argnext;
   1056 	int		print_io[2];
   1057 	char		*p, **pp;
   1058 	const char	*q;
   1059 
   1060 	if (dvi_file == NULL) {
   1061 	    WARN(XmDIALOG_ERROR, "No active dvi file");
   1062 	    return;
   1063 	}
   1064 
   1065 	/* Save state of window for next time */
   1066 	pr_save_fileradio = pr_current_fileradio;
   1067 	pr_save_pageradio = pr_current_pageradio;
   1068 
   1069 
   1070 	if (pr_save_fileradio == &r1radio1) {	/* if print to printer */
   1071 	    if (pr_save_cmd != NULL)
   1072 		free((char *) pr_save_cmd);
   1073 	    XtVaGetValues(r2text, XtNstring, &dest, NULL);
   1074 	    dest = pr_save_cmd = xstrdup(dest);
   1075 	}
   1076 	else {		/* if print to file */
   1077 	    if (pr_save_file != NULL)
   1078 		free((char *) pr_save_file);
   1079 	    XtVaGetValues(r3text, XtNstring, &dest, NULL);
   1080 	    dest = pr_save_file = xstrdup(dest);
   1081 	}
   1082 
   1083 	XtVaGetValues(r4text, XtNstring, &prn, NULL);
   1084 	if (pr_save_printer != NULL)
   1085 	    free((char *) pr_save_printer);
   1086 	if (*prn != '\0')
   1087 	    prn = pr_save_printer = xstrdup(prn);
   1088 	else
   1089 	    prn = pr_save_printer = NULL;
   1090 
   1091 	XtVaGetValues(r8text, XtNstring, &xargs, NULL);
   1092 	if (pr_save_xargs != NULL)
   1093 	    free((char *) pr_save_xargs);
   1094 	if (*xargs != '\0')
   1095 	    xargs = pr_save_xargs = xstrdup(xargs);
   1096 	else
   1097 	    xargs = pr_save_xargs = NULL;
   1098 
   1099 
   1100 	/* Open file for writing (if necessary) */
   1101 	if (pr_save_fileradio == &r1radio2) {	/* if print to file */
   1102 	    f = xfopen(dest, "w");
   1103 	    if (f == NULL) {
   1104 		WARN2(XmDIALOG_ERROR, "Error opening file: %s\n%s.", dest,
   1105 		  strerror(errno));
   1106 		return;
   1107 	    }
   1108 	}
   1109 
   1110 	/* Create popup window */
   1111 	if (printlog_shell == NULL) {
   1112 	    Widget		form;
   1113 	    int			ddist;
   1114 	    Dimension		w0, w1, w2, w3, b;
   1115 
   1116 	    XtSetSensitive(r9prev, True);
   1117 	    printlog_shell = XtVaCreatePopupShell("printlog",
   1118 	      transientShellWidgetClass, top_level,
   1119 	      XtNtitle, "Xdvi print process",
   1120 	      XtNmappedWhenManaged, False,
   1121 	      XtNtransientFor, top_level,
   1122 	      NULL);
   1123 	    form = XtCreateManagedWidget("form", formWidgetClass,
   1124 	      printlog_shell, NULL, 0);
   1125 	    printlog_text = XtVaCreateManagedWidget("text",
   1126 	      asciiTextWidgetClass, form,
   1127 	      XtNstring, "",
   1128 	      XtNdataCompression, False,
   1129 	      XtNeditType, XawtextAppend,
   1130 	      XtNscrollHorizontal, XawtextScrollAlways,
   1131 	      XtNscrollVertical, XawtextScrollAlways,
   1132 	      XtNwidth, 600,
   1133 	      XtNheight, 400,
   1134 	      XtNleft, XawChainLeft,
   1135 	      XtNright, XawChainRight,
   1136 	      XtNtop, XawChainTop,
   1137 	      XtNbottom, XawChainBottom,
   1138 	      NULL);
   1139 	    XtOverrideTranslations(printlog_text, XtParseTranslationTable(
   1140 	      "<Key>Return:printlogIntClose()\n\
   1141 ^<Key>c:printlogIntCancel()\n\
   1142 ^<Key>s:printlogIntKeep()\n\
   1143 ^<Key>q:printlogIntUnkeep()"));
   1144 
   1145 	    printlog_close = XtVaCreateManagedWidget("close",
   1146 	      commandWidgetClass, form,
   1147 	      XtNlabel, "Close window",
   1148 	      XtNsensitive, False,
   1149 	      XtNfromVert, printlog_text,
   1150 	      XtNleft, XawChainLeft,
   1151 	      XtNright, XawChainLeft,
   1152 	      XtNtop, XawChainBottom,
   1153 	      XtNbottom, XawChainBottom,
   1154 	      NULL);
   1155 	    XtAddCallback(printlog_close, XtNcallback, cb_dvips_close, NULL);
   1156 
   1157 	    printlog_keep = XtVaCreateManagedWidget("keep", toggleWidgetClass,
   1158 	      form,
   1159 	      XtNlabel, "Keep window open",
   1160 	      XtNfromVert, printlog_text,
   1161 	      XtNfromHoriz, printlog_close,
   1162 	      XtNtop, XawChainBottom,
   1163 	      XtNbottom, XawChainBottom,
   1164 	      NULL);
   1165 	    XtAddCallback(printlog_keep, XtNcallback, cb_dvips_keep, NULL);
   1166 
   1167 	    printlog_cancel = XtVaCreateManagedWidget("cancel",
   1168 	      commandWidgetClass, form,
   1169 	      XtNlabel, "Cancel",
   1170 	      XtNfromVert, printlog_text,
   1171 	      XtNfromHoriz, printlog_keep,
   1172 	      XtNleft, XawChainRight,
   1173 	      XtNright, XawChainRight,
   1174 	      XtNtop, XawChainBottom,
   1175 	      XtNbottom, XawChainBottom,
   1176 	      NULL);
   1177 	    XtAddCallback(printlog_cancel, XtNcallback, cb_dvips_cancel, NULL);
   1178 
   1179 	    /* Move the buttons to the right spots */
   1180 	    XtVaGetValues(printlog_text, XtNwidth, &w0, NULL);
   1181 	    XtVaGetValues(form, XtNdefaultDistance, &ddist, NULL);
   1182 	    XtVaGetValues(printlog_close, XtNwidth, &w1, XtNborderWidth, &b,
   1183 	      NULL);
   1184 	    w1 += 2 * b + ddist;
   1185 	    XtVaGetValues(printlog_keep, XtNwidth, &w2, XtNborderWidth, &b,
   1186 	      NULL);
   1187 	    w2 += 2 * b;
   1188 	    if (w0 > w2 + 2 * w1) {
   1189 		XtVaSetValues(printlog_keep,
   1190 		  XtNhorizDistance, (w0 - w2) / 2 - w1, NULL);
   1191 		w1 += (w0 - w2) / 2 - w1 - ddist;
   1192 	    }
   1193 	    w1 += w2;
   1194 	    XtVaGetValues(printlog_cancel, XtNwidth, &w3, XtNborderWidth, &b,
   1195 	      NULL);
   1196 	    w3 += 2 * b;
   1197 	    if (w1 + ddist + w3 < w0)
   1198 		XtVaSetValues(printlog_cancel, XtNhorizDistance, w0 - w1 - w3,
   1199 		  NULL);
   1200 
   1201 	    XdviXawRealizePopup(printlog_shell, cb_dvips_delete);
   1202 	}
   1203 	else {
   1204 	    XtVaSetValues(printlog_text, XtNstring, "", NULL);
   1205 	    XtSetSensitive(printlog_close, False);
   1206 	    XtSetSensitive(printlog_keep, True);
   1207 	    XtSetSensitive(printlog_cancel, True);
   1208 	}
   1209 
   1210 
   1211 	/* Compute length of dvips command line */
   1212 	/*   dvips [-Pps] -f [-o!command] [-p=n -lm] [xargs] */
   1213 	argc = 3;
   1214 	len = strlen("dvips") + 4;
   1215 	  /* "dvips -f\0" */
   1216 	if (prn != NULL) argc = 4, len += strlen(prn) + 3;
   1217 	if (pr_save_fileradio == &r1radio1) ++argc, len += strlen(dest) + 4;
   1218 	if (pr_save_pageradio == &r7radio) {
   1219 	    argc += 2;
   1220 	    len += uintlen(pageno1) + uintlen(pageno2) + 7;
   1221 	}
   1222 	if (pr_save_xargs != NULL) {
   1223 	    const char *q = pr_save_xargs;
   1224 
   1225 	    len += strlen(pr_save_xargs);
   1226 	    for (;;) {
   1227 		while (*q == ' ' || *q == '\t') ++q, --len;
   1228 		if (*q == '\0') break;
   1229 		++argc;
   1230 		++len;
   1231 		while (*q != '\0' && *q != ' ' && *q != '\t') ++q;
   1232 	    }
   1233 	}
   1234 
   1235 	/* Compute actual dvips command line */
   1236 	argv = xmalloc(argc * sizeof(char *));
   1237 	p = argv[0] = xmalloc(len + 1);
   1238 	argnext = argv + 1;
   1239 	p += strlen(strcpy(p, "dvips"));
   1240 	*p++ = ' ';
   1241 	*argnext++ = p;
   1242 
   1243 	if (prn != NULL) {
   1244 	    sprintf(p, "-P%s ", prn);
   1245 	    p += strlen(p);
   1246 	    *argnext++ = p;
   1247 	}
   1248 
   1249 	*p++ = '-';
   1250 	*p++ = 'f';
   1251 	*p++ = ' ';
   1252 	*argnext++ = p;
   1253 
   1254 	if (pr_save_fileradio == &r1radio1) {
   1255 	    sprintf(p, "-o!%s ", dest);
   1256 	    p += strlen(p);
   1257 	    *argnext++ = p;
   1258 	}
   1259 
   1260 	if (pr_save_pageradio == &r7radio) {
   1261 	    sprintf(p, "-p=%u ", pageno1);
   1262 	    p += strlen(p);
   1263 	    *argnext++ = p;
   1264 	    sprintf(p, "-l%u ", pageno2);
   1265 	    p += strlen(p);
   1266 	    *argnext++ = p;
   1267 	}
   1268 
   1269 	if (pr_save_xargs != NULL) {
   1270 	    const char *q = pr_save_xargs;
   1271 
   1272 	    for (;;) {
   1273 		while (*q == ' ' || *q == '\t') ++q;
   1274 		if (*q == '\0') break;
   1275 		while (*q != '\0' && *q != ' ' && *q != '\t') *p++ = *q++;
   1276 		*p++ = ' ';
   1277 		*argnext++ = p;
   1278 	    }
   1279 	}
   1280 
   1281 #if 0
   1282 	if (argnext - argv != argc)
   1283 	    errx(1, "dvips command count mismatch:  %u != %u", argc,
   1284 	      argnext - argv);
   1285 	if (p - argv[0] != len)
   1286 	    errx(1, "dvips command length mismatch:  %u != %u", len, p - argv[0]);
   1287 #endif
   1288 
   1289 	p[-1] = '\n';
   1290 
   1291 	printlog_length = 0;
   1292 
   1293 	q = rindex(dvi_name, '/');
   1294 	if (q != NULL) {
   1295 	    struct dvips_env *dep;
   1296 
   1297 	    if (dvips_envs[0].envname == NULL) {
   1298 		for (dep = dvips_envs; dep < dvips_envs + XtNumber(dvips_envs);
   1299 		  ++dep) {
   1300 		    const char *envname;
   1301 		    const char *envval;
   1302 		    unsigned int len;
   1303 
   1304 		    envval = getenv(envname = dep->en1);
   1305 		    if (envval == NULL) {
   1306 			envval = getenv(dep->en2);
   1307 			if (envval != NULL) envname = dep->en2;
   1308 		    }
   1309 		    dep->envname = envname;
   1310 		    len = q - dvi_name + 2;
   1311 		    if (envval != NULL)
   1312 			len += strlen(envval);
   1313 		    dep->envval = p = xmalloc(len);
   1314 		    bcopy(dvi_name, p, q - dvi_name);
   1315 		    p += q - dvi_name;
   1316 		    *p++ = ':';
   1317 		    *p = '\0';
   1318 		    if (envval != NULL)
   1319 			strcpy(p, envval);
   1320 		    if (setenv(dep->envname, dep->envval, 1) == -1)
   1321 			err(1, "setenv");
   1322 		}
   1323 	    }
   1324 	    for (dep = dvips_envs; dep < dvips_envs + XtNumber(dvips_envs);
   1325 	      ++dep) {
   1326 		printlog_append(dep->envname, strlen(dep->envname));
   1327 		printlog_append("=", 1);
   1328 		printlog_append(dep->envval, strlen(dep->envval));
   1329 		printlog_append(" ", 1);
   1330 	    }
   1331 	}
   1332 
   1333 	printlog_append(argv[0], len);
   1334 	do_popup(printlog_shell);
   1335 	printlog_active = True;
   1336 
   1337 	--argnext;
   1338 	for (pp = argv + 1; pp <= argnext; ++pp) (*pp)[-1] = '\0';
   1339 	*argnext = NULL;
   1340 
   1341 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, print_io) != 0) {
   1342 	    warnx("pipe");
   1343 	    return;
   1344 	}
   1345 
   1346 	/* Fork process */
   1347 	fflush(stderr);		/* avoid double buffering */
   1348 	print_child.pid = vfork();
   1349 	if (print_child.pid == 0) {		/* if child */
   1350 	    int fd;
   1351 
   1352 	    (void) close(0);
   1353 	    (void) fclose(dvi_file);
   1354 	    fd = open(dvi_name, O_RDONLY);
   1355 	    if (fd < 0) {
   1356 		perror(dvi_name);
   1357 		fflush(stderr);
   1358 		_exit(1);
   1359 	    }
   1360 	    if (fd > 0) {
   1361 		dup2(fd, 0);
   1362 		(void) close(fd);
   1363 	    }
   1364 
   1365 	    (void) close(1);
   1366 	    if (pr_save_fileradio == &r1radio1) {  /* if printing to printer */
   1367 		(void) dup2(print_io[1], 1);
   1368 	    }
   1369 	    else {	/* printing to file */
   1370 		(void) dup2(fileno(f), 1);
   1371 		(void) close(fileno(f));
   1372 	    }
   1373 	    (void) close(2);
   1374 	    (void) dup2(print_io[1], 2);
   1375 	    (void) close(print_io[1]);
   1376 	    (void) close(print_io[0]);
   1377 
   1378 	    if (setsid() == -1) {	/* so we can kill the process group */
   1379 		perror("setsid");
   1380 		fflush(stderr);
   1381 		_exit(1);
   1382 	    }
   1383 	    (void) execvp(*argv, argv);
   1384 	    warn("Execvp of '%s' failed.", *argv);
   1385 	    fflush(stderr);
   1386 	    _exit(1);
   1387 	}
   1388 
   1389 	free(argv[0]);
   1390 	free(argv);
   1391 	if (pr_save_fileradio == &r1radio2)
   1392 	    fclose(f);
   1393 
   1394 	if (print_child.pid == -1) {	/* error */
   1395 	    perror("[xdvi] vfork");
   1396 	    return;
   1397 	}
   1398 
   1399 	set_chld(&print_child);
   1400 	dvips_sig = SIGINT;
   1401 
   1402 	(void) close(print_io[1]);
   1403 
   1404 	/* Set up file descriptor for non-blocking I/O */
   1405 	fcntl(print_io[0], F_SETFL, fcntl(print_io[0], F_GETFL, 0) | O_NONBLOCK);
   1406 	if (fcntl(print_io[0], F_SETOWN, getpid()) == -1)
   1407 		warnx("fcntl F_SETOWN");
   1408 	if (fcntl(print_io[0], F_SETFL, fcntl(print_io[0], F_GETFL, 0) | FASYNC) == -1)
   1409 		warnx("fcntl F_SETFL");
   1410 
   1411 	print_xio.fd = print_io[0];
   1412 	set_io(&print_xio);
   1413 
   1414 	dvips_status = DVIPS_STAT_RUN;	/* running */
   1415 }
   1416 
   1417 
   1418 
   1419 static void
   1420 printlog_append(str, len)
   1421 	const char	*str;
   1422 	int		len;
   1423 {
   1424 	static	XawTextBlock	block	= {0, 0, NULL, 0};
   1425 
   1426 	block.ptr = (char *) str;
   1427 	block.length = len;
   1428 	block.format = XawFmt8Bit;
   1429 	while (XawTextReplace(printlog_text, printlog_length, printlog_length,
   1430 	  &block) != XawEditDone) {
   1431 	    int length;
   1432 
   1433 	    XtVaGetValues(printlog_text, XtNlength, &length, NULL);
   1434 	    printlog_length = length;
   1435 	}
   1436 	printlog_length += len;
   1437 	XawTextSetInsertionPoint(printlog_text, printlog_length);
   1438 }
   1439 
   1440 
   1441 static void
   1442 read_from_dvips()
   1443 {
   1444 	int	bytes;
   1445 	char	line[80];
   1446 
   1447 	for (;;) {
   1448 	    bytes = read(print_xio.fd, line, sizeof line);
   1449 	    if (bytes < 0) {
   1450 		if (AGAIN_CONDITION)
   1451 		    break;
   1452 		perror("xdvi: read_from_dvips");
   1453 		break;
   1454 	    }
   1455 
   1456 	    if (bytes == 0)
   1457 		break;
   1458 	    else {
   1459 		printlog_append(line, bytes);
   1460 	    }
   1461 	}
   1462 }
   1463 
   1464 static	void
   1465 cb_dvips_cancel(w, client_data, call_data)
   1466 	Widget		w;
   1467 	XtPointer	client_data;
   1468 	XtPointer	call_data;
   1469 {
   1470 	if (dvips_status != DVIPS_STAT_RUN)
   1471 	    return;	/* How did we get here? */
   1472 
   1473 	kill(print_child.pid, dvips_sig);
   1474 	dvips_sig = SIGKILL;
   1475 	printlog_append("^C", 2);
   1476 }
   1477 
   1478 static void
   1479 dvips_ended(int status)
   1480 {
   1481 	char	*str;
   1482 
   1483 	read_from_dvips();
   1484 	clear_io(&print_xio);
   1485 	(void) close(print_xio.fd);
   1486 
   1487 	if (WIFEXITED(status)) {
   1488 	    if (WEXITSTATUS(status) == 0) {
   1489 		printlog_append("Done.\n", 6);
   1490 		str = NULL;
   1491 	    }
   1492 	    else
   1493 		str = mprintf("\nPrint process returned exit code %d.\n",
   1494 		  WEXITSTATUS(status));
   1495 	}
   1496 	else if (WIFSIGNALED(status))
   1497 	    str = mprintf("\nPrint process terminated by signal %d.\n",
   1498 	      WTERMSIG(status));
   1499 	else
   1500 	    str = mprintf("\nPrint process returned unknown status 0x%x.\n",
   1501 	      status);
   1502 
   1503 	if (str != NULL) {
   1504 	    printlog_append(str, strlen(str));
   1505 	    free(str);
   1506 	}
   1507 
   1508 	printlog_act_keep(NULL, NULL, NULL, NULL);
   1509 	dvips_status = DVIPS_STAT_NONE;
   1510 
   1511 	XtSetSensitive(printlog_close, True);
   1512 	XtSetSensitive(printlog_cancel, False);
   1513 }
   1514 
   1515 static void
   1516 dvips_pop_down()
   1517 {
   1518 	XtPopdown(printlog_shell);
   1519 	printlog_active = False;
   1520 }
   1521 
   1522 static	void
   1523 cb_dvips_close(w, client_data, call_data)
   1524 	Widget		w;
   1525 	XtPointer	client_data;
   1526 	XtPointer	call_data;
   1527 {
   1528 	if (dvips_status == DVIPS_STAT_RUN)
   1529 	    return;	/* How did we get here? */
   1530 
   1531 	if (dvips_status == DVIPS_STAT_WAIT) {
   1532 	    dvips_status = DVIPS_STAT_NONE;
   1533 	}
   1534 
   1535 	dvips_pop_down();
   1536 }
   1537 
   1538 static	void
   1539 cb_dvips_keep(w, client_data, call_data)
   1540 	Widget		w;
   1541 	XtPointer	client_data;
   1542 	XtPointer	call_data;
   1543 {
   1544 	Boolean	state;
   1545 
   1546 	if (dvips_status != DVIPS_STAT_NONE)
   1547 	    return;	/* Nothing to do */
   1548 
   1549 	XtVaGetValues(printlog_keep, XtNstate, &state, NULL);
   1550 	if (!state)
   1551 	    dvips_pop_down();
   1552 }
   1553 
   1554 static	void
   1555 cb_dvips_delete(w, client_data, call_data)
   1556 	Widget		w;
   1557 	XtPointer	client_data;
   1558 	XtPointer	call_data;
   1559 {
   1560 	if (dvips_status == DVIPS_STAT_RUN)
   1561 	    cb_dvips_cancel(w, client_data, call_data);
   1562 	else
   1563 	    cb_dvips_close(w, client_data, call_data);
   1564 }
   1565 
   1566 static	void
   1567 printlog_act_close(Widget w, XEvent *event, String *params, Cardinal *num_params)
   1568 {
   1569 	cb_dvips_close(NULL, NULL, NULL);
   1570 }
   1571 
   1572 static	void
   1573 printlog_act_keep(Widget w, XEvent *event, String *params, Cardinal *num_params)
   1574 {
   1575 	XtVaSetValues(printlog_keep, XtNstate, True, NULL);
   1576 }
   1577 
   1578 static	void
   1579 printlog_act_unkeep(Widget w, XEvent *event, String *params,
   1580 	Cardinal *num_params)
   1581 {
   1582 	XtVaSetValues(printlog_keep, XtNstate, False, NULL);
   1583 	if (dvips_status == DVIPS_STAT_NONE)
   1584 	    dvips_pop_down();
   1585 }
   1586 
   1587 static	void
   1588 printlog_act_cancel(Widget w, XEvent *event, String *params,
   1589 	Cardinal *num_params)
   1590 {
   1591 	cb_dvips_cancel(NULL, NULL, NULL);
   1592 }