print.c revision 894e0ac8
1/* $XTermId: print.c,v 1.152 2014/06/13 00:36:51 tom Exp $ */
2
3/*
4 * Copyright 1997-2013,2014 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    IAttr attr = 0;
133    unsigned ch;
134    int last = MaxCols(screen);
135    int col;
136#if OPT_ISO_COLORS && OPT_PRINT_COLORS
137#define ColorOf(ld,col) (ld->color[col])
138#endif
139    unsigned fg = NO_COLOR, last_fg = NO_COLOR;
140    unsigned bg = NO_COLOR, last_bg = NO_COLOR;
141    int cs = CSET_IN;
142    int last_cs = CSET_IN;
143
144    ld = getLineData(screen, inx);
145    if (ld == 0)
146	return;
147
148    TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n",
149	   row, ROW2INX(screen, row), screen->topline, screen->max_row, chr,
150	   visibleIChars(ld->charData, (unsigned) last)));
151
152    while (last > 0) {
153	if ((ld->attribs[last - 1] & CHARDRAWN) == 0)
154	    last--;
155	else
156	    break;
157    }
158    if (last) {
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	    ch = ld->charData[col];
165#if OPT_PRINT_COLORS
166	    if (screen->colorMode) {
167		if (p->print_attributes > 1) {
168		    fg = (ld->attribs[col] & FG_COLOR)
169			? extract_fg(xw, ColorOf(ld, col), ld->attribs[col])
170			: NO_COLOR;
171		    bg = (ld->attribs[col] & BG_COLOR)
172			? extract_bg(xw, ColorOf(ld, col), ld->attribs[col])
173			: NO_COLOR;
174		}
175	    }
176#endif
177	    if ((((ld->attribs[col] & SGR_MASK) != attr)
178#if OPT_PRINT_COLORS
179		 || (last_fg != fg) || (last_bg != bg)
180#endif
181		)
182		&& ch) {
183		attr = ld->attribs[col] & SGR_MASK;
184#if OPT_PRINT_COLORS
185		last_fg = fg;
186		last_bg = bg;
187#endif
188		if (p->print_attributes)
189		    send_SGR(xw, attr, fg, bg);
190	    }
191
192	    if (ch == 0)
193		ch = ' ';
194
195#if OPT_WIDE_CHARS
196	    if (screen->utf8_mode)
197		cs = CSET_IN;
198	    else
199#endif
200		cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT;
201	    if (last_cs != cs) {
202		if (p->print_attributes) {
203		    charToPrinter(xw,
204				  (unsigned) ((cs == CSET_OUT)
205					      ? SHIFT_OUT
206					      : SHIFT_IN));
207		}
208		last_cs = cs;
209	    }
210
211	    /* FIXME:  we shouldn't have to map back from the
212	     * alternate character set, except that the
213	     * corresponding charset information is not encoded
214	     * into the CSETS array.
215	     */
216	    charToPrinter(xw,
217			  ((cs == CSET_OUT)
218			   ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f))
219			   : ch));
220	    if_OPT_WIDE_CHARS(screen, {
221		size_t off;
222		for_each_combData(off, ld) {
223		    ch = ld->combData[off][col];
224		    if (ch == 0)
225			break;
226		    charToPrinter(xw, ch);
227		}
228	    });
229	}
230	if (p->print_attributes) {
231	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
232	    if (cs != CSET_IN)
233		charToPrinter(xw, SHIFT_IN);
234	}
235    }
236
237    /* finish line (protocol for attributes needs a CR */
238    if (p->print_attributes)
239	charToPrinter(xw, '\r');
240
241    if (chr && !(p->printer_newline)) {
242	if (LineTstWrapped(ld))
243	    chr = '\0';
244    }
245
246    if (chr)
247	charToPrinter(xw, chr);
248
249    return;
250}
251
252#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0')
253
254static void
255printLines(XtermWidget xw, int top, int bot, PrinterFlags *p)
256{
257    TRACE(("printLines, rows %d..%d\n", top, bot));
258    while (top <= bot) {
259	printLine(xw, top, PrintNewLine(), p);
260	++top;
261    }
262}
263
264void
265xtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p)
266{
267    if (XtIsRealized((Widget) xw)) {
268	TScreen *screen = TScreenOf(xw);
269	Bool extent = (use_DECPEX && p->printer_extent);
270	Boolean was_open = SPS.isOpen;
271
272	printLines(xw,
273		   extent ? 0 : screen->top_marg,
274		   extent ? screen->max_row : screen->bot_marg,
275		   p);
276	if (p->printer_formfeed)
277	    charToPrinter(xw, '\f');
278
279	if (!was_open || SPS.printer_autoclose) {
280	    closePrinter(xw);
281	}
282    } else {
283	Bell(xw, XkbBI_MinorError, 0);
284    }
285}
286
287/*
288 * If p->print_everything is zero, use this behavior:
289 * If the alternate screen is active, we'll print only that.  Otherwise, print
290 * the normal screen plus all scrolled-back lines.  The distinction is made
291 * because the normal screen's buffer is part of the overall scrollback buffer.
292 *
293 * Otherwise, decode bits:
294 *	1 = current screen
295 *	2 = normal screen
296 *	4 = alternate screen
297 *	8 = saved lines
298 */
299void
300xtermPrintEverything(XtermWidget xw, PrinterFlags *p)
301{
302    TScreen *screen = TScreenOf(xw);
303    Boolean was_open = SPS.isOpen;
304    int save_which = screen->whichBuf;
305    int done_which = 0;
306
307    DEBUG_MSG("xtermPrintEverything\n");
308    if (p->print_everything) {
309	if (p->print_everything & 8) {
310	    printLines(xw, -screen->savedlines, -(screen->topline + 1), p);
311	}
312	if (p->print_everything & 4) {
313	    screen->whichBuf = 1;
314	    done_which |= 2;
315	    printLines(xw, 0, screen->max_row, p);
316	    screen->whichBuf = save_which;
317	}
318	if (p->print_everything & 2) {
319	    screen->whichBuf = 0;
320	    done_which |= 1;
321	    printLines(xw, 0, screen->max_row, p);
322	    screen->whichBuf = save_which;
323	}
324	if (p->print_everything & 1) {
325	    if (!(done_which & (1 << screen->whichBuf))) {
326		printLines(xw, 0, screen->max_row, p);
327	    }
328	}
329    } else {
330	int top = 0;
331	int bot = screen->max_row;
332	if (!screen->whichBuf) {
333	    top = -screen->savedlines - screen->topline;
334	    bot -= screen->topline;
335	}
336	printLines(xw, top, bot, p);
337    }
338    if (p->printer_formfeed)
339	charToPrinter(xw, '\f');
340
341    if (!was_open || SPS.printer_autoclose) {
342	closePrinter(xw);
343    }
344}
345
346static void
347send_CharSet(XtermWidget xw, LineData *ld)
348{
349#if OPT_DEC_CHRSET
350    const char *msg = 0;
351
352    switch (GetLineDblCS(ld)) {
353    case CSET_SWL:
354	msg = "\033#5";
355	break;
356    case CSET_DHL_TOP:
357	msg = "\033#3";
358	break;
359    case CSET_DHL_BOT:
360	msg = "\033#4";
361	break;
362    case CSET_DWL:
363	msg = "\033#6";
364	break;
365    }
366    if (msg != 0)
367	stringToPrinter(xw, msg);
368#else
369    (void) xw;
370    (void) ld;
371#endif /* OPT_DEC_CHRSET */
372}
373
374static void
375send_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg)
376{
377    char msg[80];
378
379    strcpy(msg, "\033[0");
380    if (attr & BOLD)
381	strcat(msg, ";1");
382#if OPT_WIDE_ATTRS
383    if (attr & ATR_FAINT)
384	strcat(msg, ";2");
385    if (attr & ATR_ITALIC)
386	strcat(msg, ";3");
387#endif
388    if (attr & UNDERLINE)
389	strcat(msg, ";4");	/* typo? DEC documents this as '2' */
390    if (attr & BLINK)
391	strcat(msg, ";5");
392    if (attr & INVERSE)		/* typo? DEC documents this as invisible */
393	strcat(msg, ";7");
394#if OPT_PRINT_COLORS
395    if (bg != NO_COLOR) {
396	sprintf(msg + strlen(msg), ";%u", (bg < 8) ? (40 + bg) : (92 + bg));
397    }
398    if (fg != NO_COLOR) {
399#if OPT_PC_COLORS
400	if (TScreenOf(xw)->boldColors
401	    && fg > 8
402	    && (attr & BOLD) != 0)
403	    fg -= 8;
404#endif
405	sprintf(msg + strlen(msg), ";%u", (fg < 8) ? (30 + fg) : (82 + fg));
406    }
407#else
408    (void) bg;
409    (void) fg;
410#endif
411    strcat(msg, "m");
412    stringToPrinter(xw, msg);
413}
414
415/*
416 * This implementation only knows how to write to a pipe.
417 */
418static void
419charToPrinter(XtermWidget xw, unsigned chr)
420{
421    TScreen *screen = TScreenOf(xw);
422
423    if (!SPS.isOpen && xtermHasPrinter(xw)) {
424	switch (SPS.toFile) {
425	    /*
426	     * write to a pipe.
427	     */
428	case False:
429#ifdef VMS
430	    /*
431	     * This implementation only knows how to write to a file.  When the
432	     * file is closed the print command executes.  Print command must
433	     * be of the form:
434	     *   print/que=name/delete [/otherflags].
435	     */
436	    SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w");
437#else
438	    {
439		FILE *input;
440		int my_pipe[2];
441		int c;
442		pid_t my_pid;
443
444		if (pipe(my_pipe))
445		    SysError(ERROR_FORK);
446		if ((my_pid = fork()) < 0)
447		    SysError(ERROR_FORK);
448
449		if (my_pid == 0) {
450		    DEBUG_MSG("charToPrinter: subprocess for printer\n");
451		    TRACE_CLOSE();
452		    close(my_pipe[1]);	/* printer is silent */
453		    close(screen->respond);
454
455		    close(fileno(stdout));
456		    dup2(fileno(stderr), 1);
457
458		    if (fileno(stderr) != 2) {
459			dup2(fileno(stderr), 2);
460			close(fileno(stderr));
461		    }
462
463		    /* don't want privileges! */
464		    if (xtermResetIds(screen) < 0)
465			exit(1);
466
467		    SPS.fp = popen(SPS.printer_command, "w");
468		    if (SPS.fp != 0) {
469			DEBUG_MSG("charToPrinter: opened pipe to printer\n");
470			input = fdopen(my_pipe[0], "r");
471			clearerr(input);
472			for (;;) {
473			    if (ferror(input)) {
474				DEBUG_MSG("charToPrinter: break on ferror\n");
475				break;
476			    } else if (feof(input)) {
477				DEBUG_MSG("charToPrinter: break on feof\n");
478				break;
479			    } else if ((c = fgetc(input)) == EOF) {
480				DEBUG_MSG("charToPrinter: break on EOF\n");
481				break;
482			    }
483			    fputc(c, SPS.fp);
484			    if (isForm(c))
485				fflush(SPS.fp);
486			}
487			DEBUG_MSG("charToPrinter: calling pclose\n");
488			pclose(SPS.fp);
489		    }
490		    exit(0);
491		} else {
492		    close(my_pipe[0]);	/* won't read from printer */
493		    if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) {
494			DEBUG_MSG("charToPrinter: opened printer in parent\n");
495			TRACE(("opened printer from pid %d/%d\n",
496			       (int) getpid(), (int) my_pid));
497		    } else {
498			TRACE(("failed to open printer:%s\n", strerror(errno)));
499			DEBUG_MSG("charToPrinter: could not open in parent\n");
500		    }
501		}
502	    }
503#endif
504	    break;
505	case True:
506	    TRACE(("opening \"%s\" as printer output\n", SPS.printer_command));
507	    SPS.fp = fopen(SPS.printer_command, "w");
508	    break;
509	}
510	SPS.isOpen = True;
511    }
512    if (SPS.fp != 0) {
513#if OPT_WIDE_CHARS
514	if (chr > 127) {
515	    Char temp[10];
516	    *convertToUTF8(temp, chr) = 0;
517	    fputs((char *) temp, SPS.fp);
518	} else
519#endif
520	    fputc((int) chr, SPS.fp);
521	if (isForm(chr))
522	    fflush(SPS.fp);
523    }
524}
525
526static void
527stringToPrinter(XtermWidget xw, const char *str)
528{
529    while (*str)
530	charToPrinter(xw, CharOf(*str++));
531}
532
533/*
534 * This module implements the MC (Media Copy) and related printing control
535 * sequences for VTxxx emulation.  This is based on the description in the
536 * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment
537 * Corp., March 1987).
538 */
539void
540xtermMediaControl(XtermWidget xw, int param, int private_seq)
541{
542    TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq));
543
544    if (private_seq) {
545	switch (param) {
546	case 1:
547	    printCursorLine(xw);
548	    break;
549	case 4:
550	    setPrinterControlMode(xw, 0);
551	    break;
552	case 5:
553	    setPrinterControlMode(xw, 1);
554	    break;
555	case 10:		/* VT320 */
556	    xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0));
557	    break;
558	case 11:		/* VT320 */
559	    xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
560	    break;
561	}
562    } else {
563	switch (param) {
564	case -1:
565	case 0:
566	    xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0));
567	    break;
568	case 4:
569	    setPrinterControlMode(xw, 0);
570	    break;
571	case 5:
572	    setPrinterControlMode(xw, 2);
573	    break;
574	}
575    }
576}
577
578/*
579 * When in autoprint mode, the printer prints a line from the screen when you
580 * move the cursor off that line with an LF, FF, or VT character, or an
581 * autowrap occurs.  The printed line ends with a CR and the character (LF, FF
582 * or VT) that moved the cursor off the previous line.
583 */
584void
585xtermAutoPrint(XtermWidget xw, unsigned chr)
586{
587    TScreen *screen = TScreenOf(xw);
588
589    if (SPS.printer_controlmode == 1) {
590	TRACE(("AutoPrint %d\n", chr));
591	printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0));
592	if (SPS.fp != 0)
593	    fflush(SPS.fp);
594    }
595}
596
597/*
598 * When in printer controller mode, the terminal sends received characters to
599 * the printer without displaying them on the screen. The terminal sends all
600 * characters and control sequences to the printer, except NUL, XON, XOFF, and
601 * the printer controller sequences.
602 *
603 * This function eats characters, returning 0 as long as it must buffer or
604 * divert to the printer.  We're only invoked here when in printer controller
605 * mode, and handle the exit from that mode.
606 */
607#define LB '['
608
609int
610xtermPrinterControl(XtermWidget xw, int chr)
611{
612    TScreen *screen = TScreenOf(xw);
613    /* *INDENT-OFF* */
614    static const struct {
615	const Char seq[5];
616	int active;
617    } tbl[] = {
618	{ { ANSI_CSI, '5', 'i'      }, 2 },
619	{ { ANSI_CSI, '4', 'i'      }, 0 },
620	{ { ANSI_ESC, LB,  '5', 'i' }, 2 },
621	{ { ANSI_ESC, LB,  '4', 'i' }, 0 },
622    };
623    /* *INDENT-ON* */
624
625    static Char bfr[10];
626    static size_t length;
627    size_t n;
628
629    TRACE(("In printer:%04X\n", chr));
630
631    switch (chr) {
632    case 0:
633    case CTRL('Q'):
634    case CTRL('S'):
635	return 0;		/* ignored by application */
636
637    case ANSI_CSI:
638    case ANSI_ESC:
639    case '[':
640    case '4':
641    case '5':
642    case 'i':
643	bfr[length++] = CharOf(chr);
644	for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) {
645	    size_t len = Strlen(tbl[n].seq);
646
647	    if (length == len
648		&& Strcmp(bfr, tbl[n].seq) == 0) {
649		setPrinterControlMode(xw, tbl[n].active);
650		if (SPS.printer_autoclose
651		    && SPS.printer_controlmode == 0)
652		    closePrinter(xw);
653		length = 0;
654		return 0;
655	    } else if (len > length
656		       && Strncmp(bfr, tbl[n].seq, length) == 0) {
657		return 0;
658	    }
659	}
660	length--;
661
662	/* FALLTHRU */
663
664    default:
665	for (n = 0; n < length; n++)
666	    charToPrinter(xw, bfr[n]);
667	bfr[0] = CharOf(chr);
668	length = 1;
669	return 0;
670    }
671}
672
673/*
674 * If there is no printer command, we will ignore printer controls.
675 *
676 * If we do have a printer command, we still have to verify that it will
677 * (perhaps) work if we pass it to popen().  At a minimum, the program
678 * must exist and be executable.  If not, warn and disable the feature.
679 */
680Bool
681xtermHasPrinter(XtermWidget xw)
682{
683    TScreen *screen = TScreenOf(xw);
684    Bool result = SPS.printer_checked;
685
686    if (strlen(SPS.printer_command) != 0 && !result) {
687	char **argv = x_splitargs(SPS.printer_command);
688	if (argv) {
689	    if (argv[0]) {
690		char *myShell = xtermFindShell(argv[0], False);
691		if (myShell == 0) {
692		    xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command);
693		    SPS.printer_command = x_strdup("");
694		} else {
695		    free(myShell);
696		    SPS.printer_checked = True;
697		    result = True;
698		}
699	    }
700	    x_freeargs(argv);
701	}
702	TRACE(("xtermHasPrinter:%d\n", result));
703    }
704
705    return result;
706}
707
708#define showPrinterControlMode(mode) \
709		(((mode) == 0) \
710		 ? "normal" \
711		 : ((mode) == 1 \
712		    ? "autoprint" \
713		    : "printer controller"))
714
715void
716setPrinterControlMode(XtermWidget xw, int mode)
717{
718    TScreen *screen = TScreenOf(xw);
719
720    if (xtermHasPrinter(xw)
721	&& SPS.printer_controlmode != mode) {
722	TRACE(("%s %s mode\n",
723	       (mode
724		? "set"
725		: "reset"),
726	       (mode
727		? showPrinterControlMode(mode)
728		: showPrinterControlMode(SPS.printer_controlmode))));
729	SPS.printer_controlmode = mode;
730	update_print_redir();
731    }
732}
733
734PrinterFlags *
735getPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count)
736{
737    /* *INDENT-OFF* */
738    static const struct {
739	const char *name;
740	unsigned    offset;
741	int	    value;
742    } table[] = {
743	{ "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 },
744	{ "FormFeed",	XtOffsetOf(PrinterFlags, printer_formfeed), 1 },
745	{ "noNewLine",	XtOffsetOf(PrinterFlags, printer_newline),  0 },
746	{ "NewLine",	XtOffsetOf(PrinterFlags, printer_newline),  1 },
747	{ "noAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 0 },
748	{ "monoAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 1 },
749	{ "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 },
750    };
751    /* *INDENT-ON* */
752
753    TScreen *screen = TScreenOf(xw);
754    PrinterFlags *result = &(screen->printer_flags);
755
756    TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0));
757
758    result->printer_extent = SPS.printer_extent;
759    result->printer_formfeed = SPS.printer_formfeed;
760    result->printer_newline = SPS.printer_newline;
761    result->print_attributes = SPS.print_attributes;
762    result->print_everything = SPS.print_everything;
763
764    if (param_count != 0 && *param_count != 0) {
765	Cardinal j;
766	unsigned k;
767	for (j = 0; j < *param_count; ++j) {
768	    TRACE(("param%d:%s\n", j, params[j]));
769	    for (k = 0; k < XtNumber(table); ++k) {
770		if (!x_strcasecmp(params[j], table[k].name)) {
771		    int *ptr = (int *) (void *) ((char *) result + table[k].offset);
772		    TRACE(("...PrinterFlags(%s) %d->%d\n",
773			   table[k].name,
774			   *ptr,
775			   table[k].value));
776		    *ptr = table[k].value;
777		    break;
778		}
779	    }
780	}
781    }
782
783    return result;
784}
785
786/*
787 * Print a timestamped copy of everything.
788 */
789void
790xtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs)
791{
792    TScreen *screen = TScreenOf(xw);
793    PrinterState save_state = screen->printer_state;
794    char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename));
795
796    if (my_filename != 0) {
797	unsigned save_umask = umask(0177);
798
799	timestamp_filename(my_filename, filename);
800	SPS.fp = 0;
801	SPS.isOpen = False;
802	SPS.toFile = True;
803	SPS.printer_command = my_filename;
804	SPS.printer_autoclose = True;
805	SPS.printer_formfeed = False;
806	SPS.printer_newline = True;
807	SPS.print_attributes = attrs;
808	SPS.print_everything = opts;
809	xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
810
811	umask(save_umask);
812	screen->printer_state = save_state;
813    }
814}
815
816void
817xtermPrintOnXError(XtermWidget xw, int n)
818{
819#if OPT_PRINT_ON_EXIT
820    /*
821     * The user may have requested that the contents of the screen will be
822     * written to a file if an X error occurs.
823     */
824    if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) {
825	Boolean printIt = False;
826
827	switch (n) {
828	case ERROR_XERROR:
829	    /* FALLTHRU */
830	case ERROR_XIOERROR:
831	    /* FALLTHRU */
832	case ERROR_ICEERROR:
833	    printIt = True;
834	    break;
835	}
836
837	if (printIt) {
838	    xtermPrintImmediately(xw,
839				  resource.printFileOnXError,
840				  resource.printOptsOnXError,
841				  resource.printModeOnXError);
842	}
843    }
844#else
845    (void) xw;
846    (void) n;
847#endif
848}
849