print.c revision 2e4f8982
1/* $XTermId: print.c,v 1.155 2016/05/22 16:31:59 tom Exp $ */
2
3/*
4 * Copyright 1997-2014,2016 by Thomas E. Dickey
5 *
6 *                         All Rights Reserved
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the
29 * sale, use or other dealings in this Software without prior written
30 * authorization.
31 */
32
33#include <xterm.h>
34#include <data.h>
35#include <menu.h>
36#include <error.h>
37#include <xstrings.h>
38
39#include <stdio.h>
40#include <sys/stat.h>
41
42#undef  CTRL
43#define	CTRL(c)	((c) & 0x1f)
44
45#define SHIFT_IN  '\017'
46#define SHIFT_OUT '\016'
47
48#define CSET_IN   'A'
49#define CSET_OUT  '0'
50
51#define isForm(c)      ((c) == '\r' || (c) == '\n' || (c) == '\f')
52#define Strlen(a)      strlen((const char *)a)
53#define Strcmp(a,b)    strcmp((const char *)a,(const char *)b)
54#define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c)
55
56#define SPS PrinterOf(screen)
57
58#ifdef VMS
59#define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt"
60#endif
61
62static void charToPrinter(XtermWidget /* xw */ ,
63			  unsigned /* chr */ );
64static void printLine(XtermWidget /* xw */ ,
65		      int /* row */ ,
66		      unsigned /* chr */ ,
67		      PrinterFlags * /* p */ );
68static void send_CharSet(XtermWidget /* xw */ ,
69			 LineData * /* ld */ );
70static void send_SGR(XtermWidget /* xw */ ,
71		     unsigned /* attr */ ,
72		     unsigned /* fg */ ,
73		     unsigned /* bg */ );
74static void stringToPrinter(XtermWidget /* xw */ ,
75			    const char * /*str */ );
76
77static void
78closePrinter(XtermWidget xw GCC_UNUSED)
79{
80    if (xtermHasPrinter(xw) != 0) {
81	TScreen *screen = TScreenOf(xw);
82#ifdef VMS
83	char pcommand[256];
84	(void) sprintf(pcommand, "%s %s;",
85		       SPS.printer_command,
86		       VMS_TEMP_PRINT_FILE);
87#endif
88
89	if (SPS.fp != 0) {
90	    DEBUG_MSG("closePrinter\n");
91	    pclose(SPS.fp);
92	    TRACE(("closed printer, waiting...\n"));
93#ifdef VMS			/* This is a quick hack, really should use
94				   spawn and check status or system services
95				   and go straight to the queue */
96	    (void) system(pcommand);
97#else /* VMS */
98	    while (nonblocking_wait() > 0) {
99		;
100	    }
101#endif /* VMS */
102	    SPS.fp = 0;
103	    SPS.isOpen = False;
104	    TRACE(("closed printer\n"));
105	    DEBUG_MSG("...closePrinter (done)\n");
106	}
107    }
108}
109
110static void
111printCursorLine(XtermWidget xw)
112{
113    TScreen *screen = TScreenOf(xw);
114
115    TRACE(("printCursorLine\n"));
116    printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0));
117}
118
119#define NO_COLOR	((unsigned)-1)
120
121/*
122 * DEC's manual doesn't document whether trailing blanks are removed, or what
123 * happens with a line that is entirely blank.  This function prints the
124 * characters that xterm would allow as a selection (which may include blanks).
125 */
126static void
127printLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p)
128{
129    TScreen *screen = TScreenOf(xw);
130    int inx = ROW2INX(screen, row);
131    LineData *ld;
132    int last = MaxCols(screen);
133#if OPT_ISO_COLORS && OPT_PRINT_COLORS
134#define ColorOf(ld,col) (ld->color[col])
135#endif
136    unsigned fg = NO_COLOR, last_fg = NO_COLOR;
137    unsigned bg = NO_COLOR, last_bg = NO_COLOR;
138
139    ld = getLineData(screen, inx);
140    if (ld == 0)
141	return;
142
143    TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n",
144	   row, ROW2INX(screen, row), screen->topline, screen->max_row, chr,
145	   visibleIChars(ld->charData, (unsigned) last)));
146
147    while (last > 0) {
148	if ((ld->attribs[last - 1] & CHARDRAWN) == 0)
149	    last--;
150	else
151	    break;
152    }
153
154    if (last) {
155	int col;
156	int cs = CSET_IN;
157	int last_cs = CSET_IN;
158
159	if (p->print_attributes) {
160	    send_CharSet(xw, ld);
161	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
162	}
163	for (col = 0; col < last; col++) {
164	    IAttr attr = 0;
165	    unsigned ch = ld->charData[col];
166#if OPT_PRINT_COLORS
167	    if (screen->colorMode) {
168		if (p->print_attributes > 1) {
169		    fg = (ld->attribs[col] & FG_COLOR)
170			? extract_fg(xw, ColorOf(ld, col), ld->attribs[col])
171			: NO_COLOR;
172		    bg = (ld->attribs[col] & BG_COLOR)
173			? extract_bg(xw, ColorOf(ld, col), ld->attribs[col])
174			: NO_COLOR;
175		}
176	    }
177#endif
178	    if ((((ld->attribs[col] & SGR_MASK) != attr)
179#if OPT_PRINT_COLORS
180		 || (last_fg != fg) || (last_bg != bg)
181#endif
182		)
183		&& ch) {
184		attr = ld->attribs[col] & SGR_MASK;
185#if OPT_PRINT_COLORS
186		last_fg = fg;
187		last_bg = bg;
188#endif
189		if (p->print_attributes)
190		    send_SGR(xw, attr, fg, bg);
191	    }
192
193	    if (ch == 0)
194		ch = ' ';
195
196#if OPT_WIDE_CHARS
197	    if (screen->utf8_mode)
198		cs = CSET_IN;
199	    else
200#endif
201		cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT;
202	    if (last_cs != cs) {
203		if (p->print_attributes) {
204		    charToPrinter(xw,
205				  (unsigned) ((cs == CSET_OUT)
206					      ? SHIFT_OUT
207					      : SHIFT_IN));
208		}
209		last_cs = cs;
210	    }
211
212	    /* FIXME:  we shouldn't have to map back from the
213	     * alternate character set, except that the
214	     * corresponding charset information is not encoded
215	     * into the CSETS array.
216	     */
217	    charToPrinter(xw,
218			  ((cs == CSET_OUT)
219			   ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f))
220			   : ch));
221	    if_OPT_WIDE_CHARS(screen, {
222		size_t off;
223		for_each_combData(off, ld) {
224		    ch = ld->combData[off][col];
225		    if (ch == 0)
226			break;
227		    charToPrinter(xw, ch);
228		}
229	    });
230	}
231	if (p->print_attributes) {
232	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
233	    if (cs != CSET_IN)
234		charToPrinter(xw, SHIFT_IN);
235	}
236    }
237
238    /* finish line (protocol for attributes needs a CR */
239    if (p->print_attributes)
240	charToPrinter(xw, '\r');
241
242    if (chr && !(p->printer_newline)) {
243	if (LineTstWrapped(ld))
244	    chr = '\0';
245    }
246
247    if (chr)
248	charToPrinter(xw, chr);
249
250    return;
251}
252
253#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0')
254
255static void
256printLines(XtermWidget xw, int top, int bot, PrinterFlags *p)
257{
258    TRACE(("printLines, rows %d..%d\n", top, bot));
259    while (top <= bot) {
260	printLine(xw, top, PrintNewLine(), p);
261	++top;
262    }
263}
264
265void
266xtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p)
267{
268    if (XtIsRealized((Widget) xw)) {
269	TScreen *screen = TScreenOf(xw);
270	Bool extent = (use_DECPEX && p->printer_extent);
271	Boolean was_open = SPS.isOpen;
272
273	printLines(xw,
274		   extent ? 0 : screen->top_marg,
275		   extent ? screen->max_row : screen->bot_marg,
276		   p);
277	if (p->printer_formfeed)
278	    charToPrinter(xw, '\f');
279
280	if (!was_open || SPS.printer_autoclose) {
281	    closePrinter(xw);
282	}
283    } else {
284	Bell(xw, XkbBI_MinorError, 0);
285    }
286}
287
288/*
289 * If p->print_everything is zero, use this behavior:
290 * If the alternate screen is active, we'll print only that.  Otherwise, print
291 * the normal screen plus all scrolled-back lines.  The distinction is made
292 * because the normal screen's buffer is part of the overall scrollback buffer.
293 *
294 * Otherwise, decode bits:
295 *	1 = current screen
296 *	2 = normal screen
297 *	4 = alternate screen
298 *	8 = saved lines
299 */
300void
301xtermPrintEverything(XtermWidget xw, PrinterFlags *p)
302{
303    TScreen *screen = TScreenOf(xw);
304    Boolean was_open = SPS.isOpen;
305    int save_which = screen->whichBuf;
306
307    DEBUG_MSG("xtermPrintEverything\n");
308
309    if (p->print_everything) {
310	int done_which = 0;
311
312	if (p->print_everything & 8) {
313	    printLines(xw, -screen->savedlines, -(screen->topline + 1), p);
314	}
315	if (p->print_everything & 4) {
316	    screen->whichBuf = 1;
317	    done_which |= 2;
318	    printLines(xw, 0, screen->max_row, p);
319	    screen->whichBuf = save_which;
320	}
321	if (p->print_everything & 2) {
322	    screen->whichBuf = 0;
323	    done_which |= 1;
324	    printLines(xw, 0, screen->max_row, p);
325	    screen->whichBuf = save_which;
326	}
327	if (p->print_everything & 1) {
328	    if (!(done_which & (1 << screen->whichBuf))) {
329		printLines(xw, 0, screen->max_row, p);
330	    }
331	}
332    } else {
333	int top = 0;
334	int bot = screen->max_row;
335	if (!screen->whichBuf) {
336	    top = -screen->savedlines - screen->topline;
337	    bot -= screen->topline;
338	}
339	printLines(xw, top, bot, p);
340    }
341    if (p->printer_formfeed)
342	charToPrinter(xw, '\f');
343
344    if (!was_open || SPS.printer_autoclose) {
345	closePrinter(xw);
346    }
347}
348
349static void
350send_CharSet(XtermWidget xw, LineData *ld)
351{
352#if OPT_DEC_CHRSET
353    const char *msg = 0;
354
355    switch (GetLineDblCS(ld)) {
356    case CSET_SWL:
357	msg = "\033#5";
358	break;
359    case CSET_DHL_TOP:
360	msg = "\033#3";
361	break;
362    case CSET_DHL_BOT:
363	msg = "\033#4";
364	break;
365    case CSET_DWL:
366	msg = "\033#6";
367	break;
368    }
369    if (msg != 0)
370	stringToPrinter(xw, msg);
371#else
372    (void) xw;
373    (void) ld;
374#endif /* OPT_DEC_CHRSET */
375}
376
377static void
378send_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg)
379{
380    char msg[80];
381
382    strcpy(msg, "\033[0");
383    if (attr & BOLD)
384	strcat(msg, ";1");
385#if OPT_WIDE_ATTRS
386    if (attr & ATR_FAINT)
387	strcat(msg, ";2");
388    if (attr & ATR_ITALIC)
389	strcat(msg, ";3");
390#endif
391    if (attr & UNDERLINE)
392	strcat(msg, ";4");	/* typo? DEC documents this as '2' */
393    if (attr & BLINK)
394	strcat(msg, ";5");
395    if (attr & INVERSE)		/* typo? DEC documents this as invisible */
396	strcat(msg, ";7");
397#if OPT_PRINT_COLORS
398    if (bg != NO_COLOR) {
399	sprintf(msg + strlen(msg), ";%u", (bg < 8) ? (40 + bg) : (92 + bg));
400    }
401    if (fg != NO_COLOR) {
402#if OPT_PC_COLORS
403	if (TScreenOf(xw)->boldColors
404	    && fg > 8
405	    && (attr & BOLD) != 0)
406	    fg -= 8;
407#endif
408	sprintf(msg + strlen(msg), ";%u", (fg < 8) ? (30 + fg) : (82 + fg));
409    }
410#else
411    (void) bg;
412    (void) fg;
413#endif
414    strcat(msg, "m");
415    stringToPrinter(xw, msg);
416}
417
418/*
419 * This implementation only knows how to write to a pipe.
420 */
421static void
422charToPrinter(XtermWidget xw, unsigned chr)
423{
424    TScreen *screen = TScreenOf(xw);
425
426    if (!SPS.isOpen && xtermHasPrinter(xw)) {
427	switch (SPS.toFile) {
428	    /*
429	     * write to a pipe.
430	     */
431	case False:
432#ifdef VMS
433	    /*
434	     * This implementation only knows how to write to a file.  When the
435	     * file is closed the print command executes.  Print command must
436	     * be of the form:
437	     *   print/que=name/delete [/otherflags].
438	     */
439	    SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w");
440#else
441	    {
442		int my_pipe[2];
443		pid_t my_pid;
444
445		if (pipe(my_pipe))
446		    SysError(ERROR_FORK);
447		if ((my_pid = fork()) < 0)
448		    SysError(ERROR_FORK);
449
450		if (my_pid == 0) {
451		    DEBUG_MSG("charToPrinter: subprocess for printer\n");
452		    TRACE_CLOSE();
453		    close(my_pipe[1]);	/* printer is silent */
454		    close(screen->respond);
455
456		    close(fileno(stdout));
457		    dup2(fileno(stderr), 1);
458
459		    if (fileno(stderr) != 2) {
460			dup2(fileno(stderr), 2);
461			close(fileno(stderr));
462		    }
463
464		    /* don't want privileges! */
465		    if (xtermResetIds(screen) < 0)
466			exit(1);
467
468		    SPS.fp = popen(SPS.printer_command, "w");
469		    if (SPS.fp != 0) {
470			FILE *input;
471			DEBUG_MSG("charToPrinter: opened pipe to printer\n");
472			input = fdopen(my_pipe[0], "r");
473			clearerr(input);
474
475			for (;;) {
476			    int c;
477
478			    if (ferror(input)) {
479				DEBUG_MSG("charToPrinter: break on ferror\n");
480				break;
481			    } else if (feof(input)) {
482				DEBUG_MSG("charToPrinter: break on feof\n");
483				break;
484			    } else if ((c = fgetc(input)) == EOF) {
485				DEBUG_MSG("charToPrinter: break on EOF\n");
486				break;
487			    }
488			    fputc(c, SPS.fp);
489			    if (isForm(c))
490				fflush(SPS.fp);
491			}
492
493			DEBUG_MSG("charToPrinter: calling pclose\n");
494			pclose(SPS.fp);
495		    }
496		    exit(0);
497		} else {
498		    close(my_pipe[0]);	/* won't read from printer */
499		    if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) {
500			DEBUG_MSG("charToPrinter: opened printer in parent\n");
501			TRACE(("opened printer from pid %d/%d\n",
502			       (int) getpid(), (int) my_pid));
503		    } else {
504			TRACE(("failed to open printer:%s\n", strerror(errno)));
505			DEBUG_MSG("charToPrinter: could not open in parent\n");
506		    }
507		}
508	    }
509#endif
510	    break;
511	case True:
512	    TRACE(("opening \"%s\" as printer output\n", SPS.printer_command));
513	    SPS.fp = fopen(SPS.printer_command, "w");
514	    break;
515	}
516	SPS.isOpen = True;
517    }
518    if (SPS.fp != 0) {
519#if OPT_WIDE_CHARS
520	if (chr > 127) {
521	    Char temp[10];
522	    *convertToUTF8(temp, chr) = 0;
523	    fputs((char *) temp, SPS.fp);
524	} else
525#endif
526	    fputc((int) chr, SPS.fp);
527	if (isForm(chr))
528	    fflush(SPS.fp);
529    }
530}
531
532static void
533stringToPrinter(XtermWidget xw, const char *str)
534{
535    while (*str)
536	charToPrinter(xw, CharOf(*str++));
537}
538
539/*
540 * This module implements the MC (Media Copy) and related printing control
541 * sequences for VTxxx emulation.  This is based on the description in the
542 * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment
543 * Corp., March 1987).
544 */
545void
546xtermMediaControl(XtermWidget xw, int param, int private_seq)
547{
548    TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq));
549
550    if (private_seq) {
551	switch (param) {
552	case 1:
553	    printCursorLine(xw);
554	    break;
555	case 4:
556	    setPrinterControlMode(xw, 0);
557	    break;
558	case 5:
559	    setPrinterControlMode(xw, 1);
560	    break;
561	case 10:		/* VT320 */
562	    xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0));
563	    break;
564	case 11:		/* VT320 */
565	    xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
566	    break;
567	}
568    } else {
569	switch (param) {
570	case -1:
571	case 0:
572	    xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0));
573	    break;
574	case 4:
575	    setPrinterControlMode(xw, 0);
576	    break;
577	case 5:
578	    setPrinterControlMode(xw, 2);
579	    break;
580#if OPT_SCREEN_DUMPS
581	case 10:
582	    xtermDumpHtml(xw);
583	    break;
584	case 11:
585	    xtermDumpSvg(xw);
586	    break;
587#endif
588	}
589    }
590}
591
592/*
593 * When in autoprint mode, the printer prints a line from the screen when you
594 * move the cursor off that line with an LF, FF, or VT character, or an
595 * autowrap occurs.  The printed line ends with a CR and the character (LF, FF
596 * or VT) that moved the cursor off the previous line.
597 */
598void
599xtermAutoPrint(XtermWidget xw, unsigned chr)
600{
601    TScreen *screen = TScreenOf(xw);
602
603    if (SPS.printer_controlmode == 1) {
604	TRACE(("AutoPrint %d\n", chr));
605	printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0));
606	if (SPS.fp != 0)
607	    fflush(SPS.fp);
608    }
609}
610
611/*
612 * When in printer controller mode, the terminal sends received characters to
613 * the printer without displaying them on the screen. The terminal sends all
614 * characters and control sequences to the printer, except NUL, XON, XOFF, and
615 * the printer controller sequences.
616 *
617 * This function eats characters, returning 0 as long as it must buffer or
618 * divert to the printer.  We're only invoked here when in printer controller
619 * mode, and handle the exit from that mode.
620 */
621#define LB '['
622
623int
624xtermPrinterControl(XtermWidget xw, int chr)
625{
626    TScreen *screen = TScreenOf(xw);
627    /* *INDENT-OFF* */
628    static const struct {
629	const Char seq[5];
630	int active;
631    } tbl[] = {
632	{ { ANSI_CSI, '5', 'i'      }, 2 },
633	{ { ANSI_CSI, '4', 'i'      }, 0 },
634	{ { ANSI_ESC, LB,  '5', 'i' }, 2 },
635	{ { ANSI_ESC, LB,  '4', 'i' }, 0 },
636    };
637    /* *INDENT-ON* */
638
639    static Char bfr[10];
640    static size_t length;
641    size_t n;
642
643    TRACE(("In printer:%04X\n", chr));
644
645    switch (chr) {
646    case 0:
647    case CTRL('Q'):
648    case CTRL('S'):
649	return 0;		/* ignored by application */
650
651    case ANSI_CSI:
652    case ANSI_ESC:
653    case '[':
654    case '4':
655    case '5':
656    case 'i':
657	bfr[length++] = CharOf(chr);
658	for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) {
659	    size_t len = Strlen(tbl[n].seq);
660
661	    if (length == len
662		&& Strcmp(bfr, tbl[n].seq) == 0) {
663		setPrinterControlMode(xw, tbl[n].active);
664		if (SPS.printer_autoclose
665		    && SPS.printer_controlmode == 0)
666		    closePrinter(xw);
667		length = 0;
668		return 0;
669	    } else if (len > length
670		       && Strncmp(bfr, tbl[n].seq, length) == 0) {
671		return 0;
672	    }
673	}
674	length--;
675
676	/* FALLTHRU */
677
678    default:
679	for (n = 0; n < length; n++)
680	    charToPrinter(xw, bfr[n]);
681	bfr[0] = CharOf(chr);
682	length = 1;
683	return 0;
684    }
685}
686
687/*
688 * If there is no printer command, we will ignore printer controls.
689 *
690 * If we do have a printer command, we still have to verify that it will
691 * (perhaps) work if we pass it to popen().  At a minimum, the program
692 * must exist and be executable.  If not, warn and disable the feature.
693 */
694Bool
695xtermHasPrinter(XtermWidget xw)
696{
697    TScreen *screen = TScreenOf(xw);
698    Bool result = SPS.printer_checked;
699
700    if (strlen(SPS.printer_command) != 0 && !result) {
701	char **argv = x_splitargs(SPS.printer_command);
702	if (argv) {
703	    if (argv[0]) {
704		char *myShell = xtermFindShell(argv[0], False);
705		if (myShell == 0) {
706		    xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command);
707		    SPS.printer_command = x_strdup("");
708		} else {
709		    free(myShell);
710		    SPS.printer_checked = True;
711		    result = True;
712		}
713	    }
714	    x_freeargs(argv);
715	}
716	TRACE(("xtermHasPrinter:%d\n", result));
717    }
718
719    return result;
720}
721
722#define showPrinterControlMode(mode) \
723		(((mode) == 0) \
724		 ? "normal" \
725		 : ((mode) == 1 \
726		    ? "autoprint" \
727		    : "printer controller"))
728
729void
730setPrinterControlMode(XtermWidget xw, int mode)
731{
732    TScreen *screen = TScreenOf(xw);
733
734    if (xtermHasPrinter(xw)
735	&& SPS.printer_controlmode != mode) {
736	TRACE(("%s %s mode\n",
737	       (mode
738		? "set"
739		: "reset"),
740	       (mode
741		? showPrinterControlMode(mode)
742		: showPrinterControlMode(SPS.printer_controlmode))));
743	SPS.printer_controlmode = mode;
744	update_print_redir();
745    }
746}
747
748PrinterFlags *
749getPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count)
750{
751    /* *INDENT-OFF* */
752    static const struct {
753	const char *name;
754	unsigned    offset;
755	int	    value;
756    } table[] = {
757	{ "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 },
758	{ "FormFeed",	XtOffsetOf(PrinterFlags, printer_formfeed), 1 },
759	{ "noNewLine",	XtOffsetOf(PrinterFlags, printer_newline),  0 },
760	{ "NewLine",	XtOffsetOf(PrinterFlags, printer_newline),  1 },
761	{ "noAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 0 },
762	{ "monoAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 1 },
763	{ "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 },
764    };
765    /* *INDENT-ON* */
766
767    TScreen *screen = TScreenOf(xw);
768    PrinterFlags *result = &(screen->printer_flags);
769
770    TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0));
771
772    result->printer_extent = SPS.printer_extent;
773    result->printer_formfeed = SPS.printer_formfeed;
774    result->printer_newline = SPS.printer_newline;
775    result->print_attributes = SPS.print_attributes;
776    result->print_everything = SPS.print_everything;
777
778    if (param_count != 0 && *param_count != 0) {
779	Cardinal j;
780	unsigned k;
781	for (j = 0; j < *param_count; ++j) {
782	    TRACE(("param%d:%s\n", j, params[j]));
783	    for (k = 0; k < XtNumber(table); ++k) {
784		if (!x_strcasecmp(params[j], table[k].name)) {
785		    int *ptr = (int *) (void *) ((char *) result + table[k].offset);
786		    TRACE(("...PrinterFlags(%s) %d->%d\n",
787			   table[k].name,
788			   *ptr,
789			   table[k].value));
790		    *ptr = table[k].value;
791		    break;
792		}
793	    }
794	}
795    }
796
797    return result;
798}
799
800/*
801 * Print a timestamped copy of everything.
802 */
803void
804xtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs)
805{
806    TScreen *screen = TScreenOf(xw);
807    PrinterState save_state = screen->printer_state;
808    char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename));
809
810    if (my_filename != 0) {
811	unsigned save_umask = umask(0177);
812
813	timestamp_filename(my_filename, filename);
814	SPS.fp = 0;
815	SPS.isOpen = False;
816	SPS.toFile = True;
817	SPS.printer_command = my_filename;
818	SPS.printer_autoclose = True;
819	SPS.printer_formfeed = False;
820	SPS.printer_newline = True;
821	SPS.print_attributes = attrs;
822	SPS.print_everything = opts;
823	xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
824
825	umask(save_umask);
826	screen->printer_state = save_state;
827    }
828}
829
830void
831xtermPrintOnXError(XtermWidget xw, int n)
832{
833#if OPT_PRINT_ON_EXIT
834    /*
835     * The user may have requested that the contents of the screen will be
836     * written to a file if an X error occurs.
837     */
838    if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) {
839	Boolean printIt = False;
840
841	switch (n) {
842	case ERROR_XERROR:
843	    /* FALLTHRU */
844	case ERROR_XIOERROR:
845	    /* FALLTHRU */
846	case ERROR_ICEERROR:
847	    printIt = True;
848	    break;
849	}
850
851	if (printIt) {
852	    xtermPrintImmediately(xw,
853				  resource.printFileOnXError,
854				  resource.printOptsOnXError,
855				  resource.printModeOnXError);
856	}
857    }
858#else
859    (void) xw;
860    (void) n;
861#endif
862}
863