print.c revision 04b94745
104b94745Smrg/* $XTermId: print.c,v 1.176 2023/11/24 12:16:37 tom Exp $ */
2d522f475Smrg
30bd37d32Smrg/*
404b94745Smrg * Copyright 1997-2022,2023 by Thomas E. Dickey
50bd37d32Smrg *
60bd37d32Smrg *                         All Rights Reserved
70bd37d32Smrg *
80bd37d32Smrg * Permission is hereby granted, free of charge, to any person obtaining a
90bd37d32Smrg * copy of this software and associated documentation files (the
100bd37d32Smrg * "Software"), to deal in the Software without restriction, including
110bd37d32Smrg * without limitation the rights to use, copy, modify, merge, publish,
120bd37d32Smrg * distribute, sublicense, and/or sell copies of the Software, and to
130bd37d32Smrg * permit persons to whom the Software is furnished to do so, subject to
140bd37d32Smrg * the following conditions:
150bd37d32Smrg *
160bd37d32Smrg * The above copyright notice and this permission notice shall be included
170bd37d32Smrg * in all copies or substantial portions of the Software.
180bd37d32Smrg *
190bd37d32Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
200bd37d32Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
210bd37d32Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
220bd37d32Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
230bd37d32Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
240bd37d32Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
250bd37d32Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
260bd37d32Smrg *
270bd37d32Smrg * Except as contained in this notice, the name(s) of the above copyright
280bd37d32Smrg * holders shall not be used in advertising or otherwise to promote the
290bd37d32Smrg * sale, use or other dealings in this Software without prior written
300bd37d32Smrg * authorization.
310bd37d32Smrg */
32d522f475Smrg
33d522f475Smrg#include <xterm.h>
34d522f475Smrg#include <data.h>
35d522f475Smrg#include <menu.h>
36d522f475Smrg#include <error.h>
3720d2c4d2Smrg#include <xstrings.h>
38d522f475Smrg
39d522f475Smrg#include <stdio.h>
40e39b573cSmrg#include <sys/stat.h>
41d522f475Smrg
42d522f475Smrg#undef  CTRL
43d522f475Smrg#define	CTRL(c)	((c) & 0x1f)
44d522f475Smrg
45d522f475Smrg#define SHIFT_IN  '\017'
46d522f475Smrg#define SHIFT_OUT '\016'
47d522f475Smrg
48d522f475Smrg#define CSET_IN   'A'
49d522f475Smrg#define CSET_OUT  '0'
50d522f475Smrg
51d522f475Smrg#define isForm(c)      ((c) == '\r' || (c) == '\n' || (c) == '\f')
52e39b573cSmrg#define Strlen(a)      strlen((const char *)a)
53e39b573cSmrg#define Strcmp(a,b)    strcmp((const char *)a,(const char *)b)
54e39b573cSmrg#define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c)
55e39b573cSmrg
56e39b573cSmrg#define SPS PrinterOf(screen)
57d522f475Smrg
58d522f475Smrg#ifdef VMS
59d522f475Smrg#define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt"
60d522f475Smrg#endif
61d522f475Smrg
62956cc18dSsnjstatic void charToPrinter(XtermWidget /* xw */ ,
63956cc18dSsnj			  unsigned /* chr */ );
64956cc18dSsnjstatic void printLine(XtermWidget /* xw */ ,
65956cc18dSsnj		      int /* row */ ,
6620d2c4d2Smrg		      unsigned /* chr */ ,
6720d2c4d2Smrg		      PrinterFlags * /* p */ );
68956cc18dSsnjstatic void send_CharSet(XtermWidget /* xw */ ,
69956cc18dSsnj			 LineData * /* ld */ );
70956cc18dSsnjstatic void send_SGR(XtermWidget /* xw */ ,
71956cc18dSsnj		     unsigned /* attr */ ,
72956cc18dSsnj		     unsigned /* fg */ ,
73956cc18dSsnj		     unsigned /* bg */ );
74956cc18dSsnjstatic void stringToPrinter(XtermWidget /* xw */ ,
7520d2c4d2Smrg			    const char * /*str */ );
76d522f475Smrg
77f2e35a3aSmrg#if OPT_PRINT_GRAPHICS
78f2e35a3aSmrgstatic void setGraphicsPrintToHost(XtermWidget /* xw */ ,
79f2e35a3aSmrg				   int /* enabled */ );
80f2e35a3aSmrg#else
81ad37e533Smrg#define setGraphicsPrintToHost(xw, enabled)	/* nothing */
82f2e35a3aSmrg#endif
83f2e35a3aSmrg
840bd37d32Smrgstatic void
85f2e35a3aSmrgclosePrinter(XtermWidget xw)
86d522f475Smrg{
87f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
88f2e35a3aSmrg    if (SPS.fp != 0) {
89f2e35a3aSmrg	if (SPS.toFile) {
90f2e35a3aSmrg	    fclose(SPS.fp);
91f2e35a3aSmrg	    SPS.fp = 0;
92f2e35a3aSmrg	} else if (xtermHasPrinter(xw) != 0) {
93e39b573cSmrg#ifdef VMS
94f2e35a3aSmrg	    char pcommand[256];
95f2e35a3aSmrg	    (void) sprintf(pcommand, "%s %s;",
96f2e35a3aSmrg			   SPS.printer_command,
97f2e35a3aSmrg			   VMS_TEMP_PRINT_FILE);
98d522f475Smrg#endif
99d522f475Smrg
1000bd37d32Smrg	    DEBUG_MSG("closePrinter\n");
1010bd37d32Smrg	    pclose(SPS.fp);
102d522f475Smrg	    TRACE(("closed printer, waiting...\n"));
103d522f475Smrg#ifdef VMS			/* This is a quick hack, really should use
104d522f475Smrg				   spawn and check status or system services
105d522f475Smrg				   and go straight to the queue */
106d522f475Smrg	    (void) system(pcommand);
107d522f475Smrg#else /* VMS */
1080bd37d32Smrg	    while (nonblocking_wait() > 0) {
109d522f475Smrg		;
1100bd37d32Smrg	    }
1110bd37d32Smrg#endif /* VMS */
112e39b573cSmrg	    SPS.fp = 0;
113e39b573cSmrg	    SPS.isOpen = False;
114d522f475Smrg	    TRACE(("closed printer\n"));
1150bd37d32Smrg	    DEBUG_MSG("...closePrinter (done)\n");
116d522f475Smrg	}
117d522f475Smrg    }
118d522f475Smrg}
119d522f475Smrg
120d522f475Smrgstatic void
121956cc18dSsnjprintCursorLine(XtermWidget xw)
122d522f475Smrg{
123956cc18dSsnj    TScreen *screen = TScreenOf(xw);
124d522f475Smrg
125d522f475Smrg    TRACE(("printCursorLine\n"));
12620d2c4d2Smrg    printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0));
127d522f475Smrg}
128d522f475Smrg
129d522f475Smrg/*
130d522f475Smrg * DEC's manual doesn't document whether trailing blanks are removed, or what
131d522f475Smrg * happens with a line that is entirely blank.  This function prints the
132d522f475Smrg * characters that xterm would allow as a selection (which may include blanks).
133d522f475Smrg */
134d522f475Smrgstatic void
135894e0ac8SmrgprintLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p)
136d522f475Smrg{
137956cc18dSsnj    TScreen *screen = TScreenOf(xw);
138d522f475Smrg    int inx = ROW2INX(screen, row);
139956cc18dSsnj    LineData *ld;
140d522f475Smrg    int last = MaxCols(screen);
141d522f475Smrg#if OPT_ISO_COLORS && OPT_PRINT_COLORS
142956cc18dSsnj#define ColorOf(ld,col) (ld->color[col])
143d522f475Smrg#endif
144f2e35a3aSmrg    Pixel fg = NO_COLOR;
145f2e35a3aSmrg    Pixel bg = NO_COLOR;
146913cc679Smrg#if OPT_PRINT_COLORS
147f2e35a3aSmrg    Pixel last_fg = NO_COLOR;
148f2e35a3aSmrg    Pixel last_bg = NO_COLOR;
149913cc679Smrg#endif
150d522f475Smrg
151956cc18dSsnj    ld = getLineData(screen, inx);
15220d2c4d2Smrg    if (ld == 0)
15320d2c4d2Smrg	return;
15420d2c4d2Smrg
155d522f475Smrg    TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n",
156d522f475Smrg	   row, ROW2INX(screen, row), screen->topline, screen->max_row, chr,
157956cc18dSsnj	   visibleIChars(ld->charData, (unsigned) last)));
158956cc18dSsnj
159d522f475Smrg    while (last > 0) {
160956cc18dSsnj	if ((ld->attribs[last - 1] & CHARDRAWN) == 0)
161d522f475Smrg	    last--;
162d522f475Smrg	else
163d522f475Smrg	    break;
164d522f475Smrg    }
1652e4f8982Smrg
166d522f475Smrg    if (last) {
1672e4f8982Smrg	int col;
1682e4f8982Smrg	int cs = CSET_IN;
1692e4f8982Smrg	int last_cs = CSET_IN;
1702e4f8982Smrg
17120d2c4d2Smrg	if (p->print_attributes) {
172956cc18dSsnj	    send_CharSet(xw, ld);
173956cc18dSsnj	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
174d522f475Smrg	}
175d522f475Smrg	for (col = 0; col < last; col++) {
1762e4f8982Smrg	    IAttr attr = 0;
1772e4f8982Smrg	    unsigned ch = ld->charData[col];
178d522f475Smrg#if OPT_PRINT_COLORS
179d522f475Smrg	    if (screen->colorMode) {
18020d2c4d2Smrg		if (p->print_attributes > 1) {
181956cc18dSsnj		    fg = (ld->attribs[col] & FG_COLOR)
182956cc18dSsnj			? extract_fg(xw, ColorOf(ld, col), ld->attribs[col])
183d522f475Smrg			: NO_COLOR;
184956cc18dSsnj		    bg = (ld->attribs[col] & BG_COLOR)
185956cc18dSsnj			? extract_bg(xw, ColorOf(ld, col), ld->attribs[col])
186d522f475Smrg			: NO_COLOR;
187d522f475Smrg		}
188d522f475Smrg	    }
189d522f475Smrg#endif
190f2e35a3aSmrg	    if ((((ld->attribs[col] & ATTRIBUTES) != attr)
191d522f475Smrg#if OPT_PRINT_COLORS
192d522f475Smrg		 || (last_fg != fg) || (last_bg != bg)
193d522f475Smrg#endif
194d522f475Smrg		)
195d522f475Smrg		&& ch) {
196f2e35a3aSmrg		attr = (IAttr) (ld->attribs[col] & ATTRIBUTES);
19720d2c4d2Smrg#if OPT_PRINT_COLORS
198d522f475Smrg		last_fg = fg;
199d522f475Smrg		last_bg = bg;
20020d2c4d2Smrg#endif
20120d2c4d2Smrg		if (p->print_attributes)
202f2e35a3aSmrg		    send_SGR(xw, attr, (unsigned) fg, (unsigned) bg);
203d522f475Smrg	    }
204d522f475Smrg
205d522f475Smrg	    if (ch == 0)
206d522f475Smrg		ch = ' ';
207d522f475Smrg
208d522f475Smrg#if OPT_WIDE_CHARS
209d522f475Smrg	    if (screen->utf8_mode)
210d522f475Smrg		cs = CSET_IN;
211d522f475Smrg	    else
212d522f475Smrg#endif
213d522f475Smrg		cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT;
214d522f475Smrg	    if (last_cs != cs) {
21520d2c4d2Smrg		if (p->print_attributes) {
216956cc18dSsnj		    charToPrinter(xw,
217956cc18dSsnj				  (unsigned) ((cs == CSET_OUT)
218d522f475Smrg					      ? SHIFT_OUT
219d522f475Smrg					      : SHIFT_IN));
220d522f475Smrg		}
221d522f475Smrg		last_cs = cs;
222d522f475Smrg	    }
223d522f475Smrg
224d522f475Smrg	    /* FIXME:  we shouldn't have to map back from the
225d522f475Smrg	     * alternate character set, except that the
226d522f475Smrg	     * corresponding charset information is not encoded
227d522f475Smrg	     * into the CSETS array.
228d522f475Smrg	     */
229956cc18dSsnj	    charToPrinter(xw,
230956cc18dSsnj			  ((cs == CSET_OUT)
231d522f475Smrg			   ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f))
232d522f475Smrg			   : ch));
233d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
234956cc18dSsnj		size_t off;
235956cc18dSsnj		for_each_combData(off, ld) {
236956cc18dSsnj		    ch = ld->combData[off][col];
237956cc18dSsnj		    if (ch == 0)
238d522f475Smrg			break;
239956cc18dSsnj		    charToPrinter(xw, ch);
240d522f475Smrg		}
241d522f475Smrg	    });
242d522f475Smrg	}
24320d2c4d2Smrg	if (p->print_attributes) {
244956cc18dSsnj	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
245d522f475Smrg	    if (cs != CSET_IN)
246956cc18dSsnj		charToPrinter(xw, SHIFT_IN);
247d522f475Smrg	}
248d522f475Smrg    }
24920d2c4d2Smrg
25020d2c4d2Smrg    /* finish line (protocol for attributes needs a CR */
25120d2c4d2Smrg    if (p->print_attributes)
252956cc18dSsnj	charToPrinter(xw, '\r');
25320d2c4d2Smrg
25420d2c4d2Smrg    if (chr && !(p->printer_newline)) {
25520d2c4d2Smrg	if (LineTstWrapped(ld))
25620d2c4d2Smrg	    chr = '\0';
25720d2c4d2Smrg    }
25820d2c4d2Smrg
25920d2c4d2Smrg    if (chr)
26020d2c4d2Smrg	charToPrinter(xw, chr);
261956cc18dSsnj
262956cc18dSsnj    return;
263d522f475Smrg}
264d522f475Smrg
26520d2c4d2Smrg#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0')
26620d2c4d2Smrg
267e39b573cSmrgstatic void
268894e0ac8SmrgprintLines(XtermWidget xw, int top, int bot, PrinterFlags *p)
269e39b573cSmrg{
270e39b573cSmrg    TRACE(("printLines, rows %d..%d\n", top, bot));
271e39b573cSmrg    while (top <= bot) {
272e39b573cSmrg	printLine(xw, top, PrintNewLine(), p);
273e39b573cSmrg	++top;
274e39b573cSmrg    }
275e39b573cSmrg}
276e39b573cSmrg
277d522f475Smrgvoid
278894e0ac8SmrgxtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p)
279d522f475Smrg{
280956cc18dSsnj    if (XtIsRealized((Widget) xw)) {
281956cc18dSsnj	TScreen *screen = TScreenOf(xw);
28220d2c4d2Smrg	Bool extent = (use_DECPEX && p->printer_extent);
283e39b573cSmrg	Boolean was_open = SPS.isOpen;
284d522f475Smrg
285e39b573cSmrg	printLines(xw,
286e39b573cSmrg		   extent ? 0 : screen->top_marg,
287e39b573cSmrg		   extent ? screen->max_row : screen->bot_marg,
288e39b573cSmrg		   p);
28920d2c4d2Smrg	if (p->printer_formfeed)
290956cc18dSsnj	    charToPrinter(xw, '\f');
291d522f475Smrg
292e39b573cSmrg	if (!was_open || SPS.printer_autoclose) {
293956cc18dSsnj	    closePrinter(xw);
294d522f475Smrg	}
295d522f475Smrg    } else {
29620d2c4d2Smrg	Bell(xw, XkbBI_MinorError, 0);
297d522f475Smrg    }
298d522f475Smrg}
299d522f475Smrg
300d522f475Smrg/*
301e39b573cSmrg * If p->print_everything is zero, use this behavior:
302d522f475Smrg * If the alternate screen is active, we'll print only that.  Otherwise, print
303d522f475Smrg * the normal screen plus all scrolled-back lines.  The distinction is made
304d522f475Smrg * because the normal screen's buffer is part of the overall scrollback buffer.
305e39b573cSmrg *
306e39b573cSmrg * Otherwise, decode bits:
307e39b573cSmrg *	1 = current screen
308e39b573cSmrg *	2 = normal screen
309e39b573cSmrg *	4 = alternate screen
310e39b573cSmrg *	8 = saved lines
311d522f475Smrg */
312956cc18dSsnjvoid
313894e0ac8SmrgxtermPrintEverything(XtermWidget xw, PrinterFlags *p)
314d522f475Smrg{
315956cc18dSsnj    TScreen *screen = TScreenOf(xw);
316e39b573cSmrg    Boolean was_open = SPS.isOpen;
317e39b573cSmrg    int save_which = screen->whichBuf;
318d522f475Smrg
3190bd37d32Smrg    DEBUG_MSG("xtermPrintEverything\n");
3202e4f8982Smrg
321e39b573cSmrg    if (p->print_everything) {
3222e4f8982Smrg	int done_which = 0;
3232e4f8982Smrg
324e39b573cSmrg	if (p->print_everything & 8) {
325e39b573cSmrg	    printLines(xw, -screen->savedlines, -(screen->topline + 1), p);
326e39b573cSmrg	}
327e39b573cSmrg	if (p->print_everything & 4) {
3285307cd1aSmrg	    SwitchBufPtrs(xw, 1);
329e39b573cSmrg	    done_which |= 2;
330e39b573cSmrg	    printLines(xw, 0, screen->max_row, p);
3315307cd1aSmrg	    SwitchBufPtrs(xw, save_which);
332e39b573cSmrg	}
333e39b573cSmrg	if (p->print_everything & 2) {
3345307cd1aSmrg	    SwitchBufPtrs(xw, 0);
335e39b573cSmrg	    done_which |= 1;
336e39b573cSmrg	    printLines(xw, 0, screen->max_row, p);
3375307cd1aSmrg	    SwitchBufPtrs(xw, save_which);
338e39b573cSmrg	}
339e39b573cSmrg	if (p->print_everything & 1) {
340e39b573cSmrg	    if (!(done_which & (1 << screen->whichBuf))) {
341e39b573cSmrg		printLines(xw, 0, screen->max_row, p);
342e39b573cSmrg	    }
343e39b573cSmrg	}
344e39b573cSmrg    } else {
345e39b573cSmrg	int top = 0;
346e39b573cSmrg	int bot = screen->max_row;
347e39b573cSmrg	if (!screen->whichBuf) {
348e39b573cSmrg	    top = -screen->savedlines - screen->topline;
349e39b573cSmrg	    bot -= screen->topline;
350e39b573cSmrg	}
351e39b573cSmrg	printLines(xw, top, bot, p);
35220d2c4d2Smrg    }
35320d2c4d2Smrg    if (p->printer_formfeed)
354956cc18dSsnj	charToPrinter(xw, '\f');
355d522f475Smrg
356e39b573cSmrg    if (!was_open || SPS.printer_autoclose) {
357956cc18dSsnj	closePrinter(xw);
358d522f475Smrg    }
359d522f475Smrg}
360d522f475Smrg
361d522f475Smrgstatic void
362894e0ac8Smrgsend_CharSet(XtermWidget xw, LineData *ld)
363d522f475Smrg{
364d522f475Smrg#if OPT_DEC_CHRSET
36520d2c4d2Smrg    const char *msg = 0;
366d522f475Smrg
367956cc18dSsnj    switch (GetLineDblCS(ld)) {
368d522f475Smrg    case CSET_SWL:
369d522f475Smrg	msg = "\033#5";
370d522f475Smrg	break;
371d522f475Smrg    case CSET_DHL_TOP:
372d522f475Smrg	msg = "\033#3";
373d522f475Smrg	break;
374d522f475Smrg    case CSET_DHL_BOT:
375d522f475Smrg	msg = "\033#4";
376d522f475Smrg	break;
377d522f475Smrg    case CSET_DWL:
378d522f475Smrg	msg = "\033#6";
379d522f475Smrg	break;
380d522f475Smrg    }
381d522f475Smrg    if (msg != 0)
382956cc18dSsnj	stringToPrinter(xw, msg);
383d522f475Smrg#else
384956cc18dSsnj    (void) xw;
385956cc18dSsnj    (void) ld;
386d522f475Smrg#endif /* OPT_DEC_CHRSET */
387d522f475Smrg}
388d522f475Smrg
389d522f475Smrgstatic void
390956cc18dSsnjsend_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg)
391d522f475Smrg{
392d522f475Smrg    char msg[80];
393e39b573cSmrg
394f2e35a3aSmrg#if OPT_ISO_COLORS && OPT_PC_COLORS
395f2e35a3aSmrg    if ((attr & FG_COLOR) && (fg != NO_COLOR)) {
39620d2c4d2Smrg	if (TScreenOf(xw)->boldColors
397d522f475Smrg	    && fg > 8
39804b94745Smrg	    && fg < 16
399d522f475Smrg	    && (attr & BOLD) != 0)
400d522f475Smrg	    fg -= 8;
401d522f475Smrg    }
402d522f475Smrg#endif
403f2e35a3aSmrg    strcpy(msg, "\033[");
404f2e35a3aSmrg    xtermFormatSGR(xw, msg + strlen(msg), attr, (int) fg, (int) bg);
405d522f475Smrg    strcat(msg, "m");
406956cc18dSsnj    stringToPrinter(xw, msg);
407d522f475Smrg}
408d522f475Smrg
409d522f475Smrg/*
410d522f475Smrg * This implementation only knows how to write to a pipe.
411d522f475Smrg */
412d522f475Smrgstatic void
413956cc18dSsnjcharToPrinter(XtermWidget xw, unsigned chr)
414d522f475Smrg{
415956cc18dSsnj    TScreen *screen = TScreenOf(xw);
416d522f475Smrg
41704b94745Smrg#if OPT_WIDE_CHARS
41804b94745Smrg    if (screen->wide_chars && screen->utf8_mode) {
41904b94745Smrg	if (chr == UCS_REPL) {
42004b94745Smrg	    stringToPrinter(xw, screen->default_string);
42104b94745Smrg	    return;
42204b94745Smrg	}
42304b94745Smrg    }
42404b94745Smrg#endif
42504b94745Smrg    if (is_NON_CHAR(chr))
42604b94745Smrg	return;
42704b94745Smrg
428f2e35a3aSmrg    if (!SPS.isOpen && (SPS.toFile || xtermHasPrinter(xw))) {
429e39b573cSmrg	switch (SPS.toFile) {
430e39b573cSmrg	    /*
431e39b573cSmrg	     * write to a pipe.
432e39b573cSmrg	     */
433e39b573cSmrg	case False:
434e39b573cSmrg#ifdef VMS
435e39b573cSmrg	    /*
436e39b573cSmrg	     * This implementation only knows how to write to a file.  When the
437e39b573cSmrg	     * file is closed the print command executes.  Print command must
438e39b573cSmrg	     * be of the form:
439f2e35a3aSmrg	     *   print/queue=name/delete [/otherflags].
440e39b573cSmrg	     */
441e39b573cSmrg	    SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w");
442d522f475Smrg#else
443e39b573cSmrg	    {
444e39b573cSmrg		int my_pipe[2];
445e39b573cSmrg		pid_t my_pid;
446e39b573cSmrg
447e39b573cSmrg		if (pipe(my_pipe))
448e39b573cSmrg		    SysError(ERROR_FORK);
449e39b573cSmrg		if ((my_pid = fork()) < 0)
450e39b573cSmrg		    SysError(ERROR_FORK);
451e39b573cSmrg
452e39b573cSmrg		if (my_pid == 0) {
4530bd37d32Smrg		    DEBUG_MSG("charToPrinter: subprocess for printer\n");
454e39b573cSmrg		    TRACE_CLOSE();
455e39b573cSmrg		    close(my_pipe[1]);	/* printer is silent */
456e39b573cSmrg		    close(screen->respond);
457e39b573cSmrg
458e39b573cSmrg		    close(fileno(stdout));
459e39b573cSmrg		    dup2(fileno(stderr), 1);
460e39b573cSmrg
461e39b573cSmrg		    if (fileno(stderr) != 2) {
462e39b573cSmrg			dup2(fileno(stderr), 2);
463e39b573cSmrg			close(fileno(stderr));
464e39b573cSmrg		    }
465e39b573cSmrg
466e39b573cSmrg		    /* don't want privileges! */
467e39b573cSmrg		    if (xtermResetIds(screen) < 0)
46804b94745Smrg			exit(ERROR_MISC);
469e39b573cSmrg
470e39b573cSmrg		    SPS.fp = popen(SPS.printer_command, "w");
4710bd37d32Smrg		    if (SPS.fp != 0) {
4722e4f8982Smrg			FILE *input;
4730bd37d32Smrg			DEBUG_MSG("charToPrinter: opened pipe to printer\n");
474913cc679Smrg			if ((input = fdopen(my_pipe[0], "r")) != 0) {
475913cc679Smrg			    clearerr(input);
476913cc679Smrg
477913cc679Smrg			    for (;;) {
478913cc679Smrg				int c;
479913cc679Smrg
480913cc679Smrg				if (ferror(input)) {
481913cc679Smrg				    DEBUG_MSG("charToPrinter: break on ferror\n");
482913cc679Smrg				    break;
483913cc679Smrg				} else if (feof(input)) {
484913cc679Smrg				    DEBUG_MSG("charToPrinter: break on feof\n");
485913cc679Smrg				    break;
486913cc679Smrg				} else if ((c = fgetc(input)) == EOF) {
487913cc679Smrg				    DEBUG_MSG("charToPrinter: break on EOF\n");
488913cc679Smrg				    break;
489913cc679Smrg				}
490913cc679Smrg				fputc(c, SPS.fp);
491913cc679Smrg				if (isForm(c))
492913cc679Smrg				    fflush(SPS.fp);
4930bd37d32Smrg			    }
4940bd37d32Smrg			}
4950bd37d32Smrg			DEBUG_MSG("charToPrinter: calling pclose\n");
4960bd37d32Smrg			pclose(SPS.fp);
497913cc679Smrg			if (input)
498913cc679Smrg			    fclose(input);
499e39b573cSmrg		    }
500e39b573cSmrg		    exit(0);
501e39b573cSmrg		} else {
502e39b573cSmrg		    close(my_pipe[0]);	/* won't read from printer */
5030bd37d32Smrg		    if ((SPS.fp = fdopen(my_pipe[1], "w")) != 0) {
5040bd37d32Smrg			DEBUG_MSG("charToPrinter: opened printer in parent\n");
5050bd37d32Smrg			TRACE(("opened printer from pid %d/%d\n",
5060bd37d32Smrg			       (int) getpid(), (int) my_pid));
5070bd37d32Smrg		    } else {
5080bd37d32Smrg			TRACE(("failed to open printer:%s\n", strerror(errno)));
5090bd37d32Smrg			DEBUG_MSG("charToPrinter: could not open in parent\n");
5100bd37d32Smrg		    }
511e39b573cSmrg		}
512d522f475Smrg	    }
513d522f475Smrg#endif
514e39b573cSmrg	    break;
515e39b573cSmrg	case True:
516e39b573cSmrg	    TRACE(("opening \"%s\" as printer output\n", SPS.printer_command));
517e39b573cSmrg	    SPS.fp = fopen(SPS.printer_command, "w");
518e39b573cSmrg	    break;
519e39b573cSmrg	}
520e39b573cSmrg	SPS.isOpen = True;
521d522f475Smrg    }
522e39b573cSmrg    if (SPS.fp != 0) {
523d522f475Smrg#if OPT_WIDE_CHARS
524d522f475Smrg	if (chr > 127) {
525d522f475Smrg	    Char temp[10];
526d522f475Smrg	    *convertToUTF8(temp, chr) = 0;
527e39b573cSmrg	    fputs((char *) temp, SPS.fp);
528d522f475Smrg	} else
529d522f475Smrg#endif
530e39b573cSmrg	    fputc((int) chr, SPS.fp);
531d522f475Smrg	if (isForm(chr))
532e39b573cSmrg	    fflush(SPS.fp);
533d522f475Smrg    }
534d522f475Smrg}
535d522f475Smrg
536d522f475Smrgstatic void
53720d2c4d2SmrgstringToPrinter(XtermWidget xw, const char *str)
538d522f475Smrg{
539d522f475Smrg    while (*str)
540956cc18dSsnj	charToPrinter(xw, CharOf(*str++));
541d522f475Smrg}
542d522f475Smrg
543d522f475Smrg/*
544d522f475Smrg * This module implements the MC (Media Copy) and related printing control
545d522f475Smrg * sequences for VTxxx emulation.  This is based on the description in the
546d522f475Smrg * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment
547d522f475Smrg * Corp., March 1987).
548d522f475Smrg */
549d522f475Smrgvoid
550956cc18dSsnjxtermMediaControl(XtermWidget xw, int param, int private_seq)
551d522f475Smrg{
552d522f475Smrg    TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq));
553d522f475Smrg
554d522f475Smrg    if (private_seq) {
555d522f475Smrg	switch (param) {
556f2e35a3aSmrg	case -1:
557f2e35a3aSmrg	case 0:		/* VT125 */
558f2e35a3aSmrg	    setGraphicsPrintToHost(xw, 0);	/* graphics to printer */
559f2e35a3aSmrg	    break;
560d522f475Smrg	case 1:
561956cc18dSsnj	    printCursorLine(xw);
562d522f475Smrg	    break;
563f2e35a3aSmrg	case 2:		/* VT125 */
564f2e35a3aSmrg	    setGraphicsPrintToHost(xw, 1);	/* graphics to host */
565f2e35a3aSmrg	    break;
566d522f475Smrg	case 4:
567f2e35a3aSmrg	    setPrinterControlMode(xw, 0);	/* autoprint disable */
568d522f475Smrg	    break;
569d522f475Smrg	case 5:
570f2e35a3aSmrg	    setPrinterControlMode(xw, 1);	/* autoprint enable */
571d522f475Smrg	    break;
572d522f475Smrg	case 10:		/* VT320 */
573f2e35a3aSmrg	    /* print whole screen, across sessions */
57420d2c4d2Smrg	    xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0));
575d522f475Smrg	    break;
576d522f475Smrg	case 11:		/* VT320 */
577f2e35a3aSmrg	    /* print all pages in current session */
57820d2c4d2Smrg	    xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
579d522f475Smrg	    break;
580d522f475Smrg	}
581d522f475Smrg    } else {
582d522f475Smrg	switch (param) {
583d522f475Smrg	case -1:
584d522f475Smrg	case 0:
58520d2c4d2Smrg	    xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0));
586d522f475Smrg	    break;
587d522f475Smrg	case 4:
588f2e35a3aSmrg	    setPrinterControlMode(xw, 0);	/* printer controller mode off */
589d522f475Smrg	    break;
590d522f475Smrg	case 5:
591f2e35a3aSmrg	    setPrinterControlMode(xw, 2);	/* printer controller mode on */
592d522f475Smrg	    break;
5932e4f8982Smrg#if OPT_SCREEN_DUMPS
5942e4f8982Smrg	case 10:
5952e4f8982Smrg	    xtermDumpHtml(xw);
5962e4f8982Smrg	    break;
5972e4f8982Smrg	case 11:
5982e4f8982Smrg	    xtermDumpSvg(xw);
5992e4f8982Smrg	    break;
6002e4f8982Smrg#endif
601d522f475Smrg	}
602d522f475Smrg    }
603d522f475Smrg}
604d522f475Smrg
605d522f475Smrg/*
606d522f475Smrg * When in autoprint mode, the printer prints a line from the screen when you
607d522f475Smrg * move the cursor off that line with an LF, FF, or VT character, or an
608d522f475Smrg * autowrap occurs.  The printed line ends with a CR and the character (LF, FF
609d522f475Smrg * or VT) that moved the cursor off the previous line.
610d522f475Smrg */
611d522f475Smrgvoid
612956cc18dSsnjxtermAutoPrint(XtermWidget xw, unsigned chr)
613d522f475Smrg{
614956cc18dSsnj    TScreen *screen = TScreenOf(xw);
615d522f475Smrg
616e39b573cSmrg    if (SPS.printer_controlmode == 1) {
617d522f475Smrg	TRACE(("AutoPrint %d\n", chr));
61820d2c4d2Smrg	printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0));
619e39b573cSmrg	if (SPS.fp != 0)
620e39b573cSmrg	    fflush(SPS.fp);
621d522f475Smrg    }
622d522f475Smrg}
623d522f475Smrg
624d522f475Smrg/*
625d522f475Smrg * When in printer controller mode, the terminal sends received characters to
626d522f475Smrg * the printer without displaying them on the screen. The terminal sends all
627d522f475Smrg * characters and control sequences to the printer, except NUL, XON, XOFF, and
628d522f475Smrg * the printer controller sequences.
629d522f475Smrg *
630d522f475Smrg * This function eats characters, returning 0 as long as it must buffer or
631d522f475Smrg * divert to the printer.  We're only invoked here when in printer controller
632d522f475Smrg * mode, and handle the exit from that mode.
633d522f475Smrg */
634d522f475Smrg#define LB '['
635d522f475Smrg
636d522f475Smrgint
637956cc18dSsnjxtermPrinterControl(XtermWidget xw, int chr)
638d522f475Smrg{
639956cc18dSsnj    TScreen *screen = TScreenOf(xw);
640d522f475Smrg    /* *INDENT-OFF* */
641e39b573cSmrg    static const struct {
642e39b573cSmrg	const Char seq[5];
643d522f475Smrg	int active;
644d522f475Smrg    } tbl[] = {
645d522f475Smrg	{ { ANSI_CSI, '5', 'i'      }, 2 },
646d522f475Smrg	{ { ANSI_CSI, '4', 'i'      }, 0 },
647d522f475Smrg	{ { ANSI_ESC, LB,  '5', 'i' }, 2 },
648d522f475Smrg	{ { ANSI_ESC, LB,  '4', 'i' }, 0 },
649d522f475Smrg    };
650d522f475Smrg    /* *INDENT-ON* */
651d522f475Smrg
652d522f475Smrg    static Char bfr[10];
653d522f475Smrg    static size_t length;
654d522f475Smrg    size_t n;
655d522f475Smrg
656d522f475Smrg    TRACE(("In printer:%04X\n", chr));
657d522f475Smrg
658d522f475Smrg    switch (chr) {
659d522f475Smrg    case 0:
660d522f475Smrg    case CTRL('Q'):
661d522f475Smrg    case CTRL('S'):
662d522f475Smrg	return 0;		/* ignored by application */
663d522f475Smrg
664d522f475Smrg    case ANSI_CSI:
665d522f475Smrg    case ANSI_ESC:
666d522f475Smrg    case '[':
667d522f475Smrg    case '4':
668d522f475Smrg    case '5':
669d522f475Smrg    case 'i':
6704e40088cSchristos	bfr[length++] = CharOf(chr);
671d522f475Smrg	for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) {
672d522f475Smrg	    size_t len = Strlen(tbl[n].seq);
673d522f475Smrg
674d522f475Smrg	    if (length == len
675d522f475Smrg		&& Strcmp(bfr, tbl[n].seq) == 0) {
676956cc18dSsnj		setPrinterControlMode(xw, tbl[n].active);
677e39b573cSmrg		if (SPS.printer_autoclose
678e39b573cSmrg		    && SPS.printer_controlmode == 0)
679956cc18dSsnj		    closePrinter(xw);
680d522f475Smrg		length = 0;
681d522f475Smrg		return 0;
682d522f475Smrg	    } else if (len > length
683d522f475Smrg		       && Strncmp(bfr, tbl[n].seq, length) == 0) {
684d522f475Smrg		return 0;
685d522f475Smrg	    }
686d522f475Smrg	}
687d522f475Smrg	length--;
688d522f475Smrg
689d522f475Smrg	/* FALLTHRU */
690d522f475Smrg
691d522f475Smrg    default:
692d522f475Smrg	for (n = 0; n < length; n++)
693956cc18dSsnj	    charToPrinter(xw, bfr[n]);
6944e40088cSchristos	bfr[0] = CharOf(chr);
695d522f475Smrg	length = 1;
696d522f475Smrg	return 0;
697d522f475Smrg    }
698d522f475Smrg}
699d522f475Smrg
700d522f475Smrg/*
701d522f475Smrg * If there is no printer command, we will ignore printer controls.
7020bd37d32Smrg *
7030bd37d32Smrg * If we do have a printer command, we still have to verify that it will
7040bd37d32Smrg * (perhaps) work if we pass it to popen().  At a minimum, the program
7050bd37d32Smrg * must exist and be executable.  If not, warn and disable the feature.
706d522f475Smrg */
707d522f475SmrgBool
708956cc18dSsnjxtermHasPrinter(XtermWidget xw)
709d522f475Smrg{
710956cc18dSsnj    TScreen *screen = TScreenOf(xw);
7110bd37d32Smrg    Bool result = SPS.printer_checked;
7120bd37d32Smrg
7130bd37d32Smrg    if (strlen(SPS.printer_command) != 0 && !result) {
7140bd37d32Smrg	char **argv = x_splitargs(SPS.printer_command);
7150bd37d32Smrg	if (argv) {
7160bd37d32Smrg	    if (argv[0]) {
7170bd37d32Smrg		char *myShell = xtermFindShell(argv[0], False);
7180bd37d32Smrg		if (myShell == 0) {
7190bd37d32Smrg		    xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command);
7200bd37d32Smrg		    SPS.printer_command = x_strdup("");
7210bd37d32Smrg		} else {
7220bd37d32Smrg		    free(myShell);
7230bd37d32Smrg		    SPS.printer_checked = True;
7240bd37d32Smrg		    result = True;
7250bd37d32Smrg		}
7260bd37d32Smrg	    }
7270bd37d32Smrg	    x_freeargs(argv);
7280bd37d32Smrg	}
7290bd37d32Smrg	TRACE(("xtermHasPrinter:%d\n", result));
7300bd37d32Smrg    }
731d522f475Smrg
7320bd37d32Smrg    return result;
733d522f475Smrg}
734d522f475Smrg
735f2e35a3aSmrg#if OPT_PRINT_GRAPHICS
736f2e35a3aSmrgstatic void
737f2e35a3aSmrgsetGraphicsPrintToHost(XtermWidget xw, int enabled)
738f2e35a3aSmrg{
739f2e35a3aSmrg    TScreen *screen = TScreenOf(xw);
740f2e35a3aSmrg
741f2e35a3aSmrg    TRACE(("graphics print to host enabled=%d\n", enabled));
742f2e35a3aSmrg    screen->graphics_print_to_host = (Boolean) enabled;
743f2e35a3aSmrg}
744f2e35a3aSmrg#endif
745f2e35a3aSmrg
746d522f475Smrg#define showPrinterControlMode(mode) \
747d522f475Smrg		(((mode) == 0) \
748d522f475Smrg		 ? "normal" \
749d522f475Smrg		 : ((mode) == 1 \
750d522f475Smrg		    ? "autoprint" \
751d522f475Smrg		    : "printer controller"))
752d522f475Smrg
753d522f475Smrgvoid
754956cc18dSsnjsetPrinterControlMode(XtermWidget xw, int mode)
755d522f475Smrg{
75620d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
75720d2c4d2Smrg
758956cc18dSsnj    if (xtermHasPrinter(xw)
759e39b573cSmrg	&& SPS.printer_controlmode != mode) {
760d522f475Smrg	TRACE(("%s %s mode\n",
761d522f475Smrg	       (mode
762d522f475Smrg		? "set"
763d522f475Smrg		: "reset"),
764d522f475Smrg	       (mode
765d522f475Smrg		? showPrinterControlMode(mode)
766e39b573cSmrg		: showPrinterControlMode(SPS.printer_controlmode))));
767e39b573cSmrg	SPS.printer_controlmode = mode;
768d522f475Smrg	update_print_redir();
769d522f475Smrg    }
770d522f475Smrg}
77120d2c4d2Smrg
77220d2c4d2SmrgPrinterFlags *
773894e0ac8SmrggetPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count)
77420d2c4d2Smrg{
77520d2c4d2Smrg    /* *INDENT-OFF* */
77620d2c4d2Smrg    static const struct {
77720d2c4d2Smrg	const char *name;
77820d2c4d2Smrg	unsigned    offset;
77920d2c4d2Smrg	int	    value;
78020d2c4d2Smrg    } table[] = {
78120d2c4d2Smrg	{ "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 },
78220d2c4d2Smrg	{ "FormFeed",	XtOffsetOf(PrinterFlags, printer_formfeed), 1 },
78320d2c4d2Smrg	{ "noNewLine",	XtOffsetOf(PrinterFlags, printer_newline),  0 },
78420d2c4d2Smrg	{ "NewLine",	XtOffsetOf(PrinterFlags, printer_newline),  1 },
78520d2c4d2Smrg	{ "noAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 0 },
78620d2c4d2Smrg	{ "monoAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 1 },
78720d2c4d2Smrg	{ "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 },
78820d2c4d2Smrg    };
78920d2c4d2Smrg    /* *INDENT-ON* */
79020d2c4d2Smrg
79120d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
79220d2c4d2Smrg    PrinterFlags *result = &(screen->printer_flags);
79320d2c4d2Smrg
79420d2c4d2Smrg    TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0));
79520d2c4d2Smrg
796e39b573cSmrg    result->printer_extent = SPS.printer_extent;
797e39b573cSmrg    result->printer_formfeed = SPS.printer_formfeed;
798e39b573cSmrg    result->printer_newline = SPS.printer_newline;
799e39b573cSmrg    result->print_attributes = SPS.print_attributes;
800e39b573cSmrg    result->print_everything = SPS.print_everything;
80120d2c4d2Smrg
80220d2c4d2Smrg    if (param_count != 0 && *param_count != 0) {
80320d2c4d2Smrg	Cardinal j;
80420d2c4d2Smrg	unsigned k;
80520d2c4d2Smrg	for (j = 0; j < *param_count; ++j) {
80620d2c4d2Smrg	    TRACE(("param%d:%s\n", j, params[j]));
80720d2c4d2Smrg	    for (k = 0; k < XtNumber(table); ++k) {
80820d2c4d2Smrg		if (!x_strcasecmp(params[j], table[k].name)) {
809a1f3da82Smrg		    int *ptr = (int *) (void *) ((char *) result + table[k].offset);
81020d2c4d2Smrg		    TRACE(("...PrinterFlags(%s) %d->%d\n",
81120d2c4d2Smrg			   table[k].name,
81220d2c4d2Smrg			   *ptr,
81320d2c4d2Smrg			   table[k].value));
81420d2c4d2Smrg		    *ptr = table[k].value;
81520d2c4d2Smrg		    break;
81620d2c4d2Smrg		}
81720d2c4d2Smrg	    }
81820d2c4d2Smrg	}
81920d2c4d2Smrg    }
82020d2c4d2Smrg
82120d2c4d2Smrg    return result;
82220d2c4d2Smrg}
823e39b573cSmrg
824e39b573cSmrg/*
825e39b573cSmrg * Print a timestamped copy of everything.
826e39b573cSmrg */
827e39b573cSmrgvoid
828e39b573cSmrgxtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs)
829e39b573cSmrg{
830e39b573cSmrg    TScreen *screen = TScreenOf(xw);
831e39b573cSmrg    PrinterState save_state = screen->printer_state;
832e39b573cSmrg    char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename));
833e39b573cSmrg
834e39b573cSmrg    if (my_filename != 0) {
835913cc679Smrg	mode_t save_umask = umask(0177);
836e39b573cSmrg
837e39b573cSmrg	timestamp_filename(my_filename, filename);
838e39b573cSmrg	SPS.fp = 0;
839e39b573cSmrg	SPS.isOpen = False;
840e39b573cSmrg	SPS.toFile = True;
841e39b573cSmrg	SPS.printer_command = my_filename;
842e39b573cSmrg	SPS.printer_autoclose = True;
843e39b573cSmrg	SPS.printer_formfeed = False;
844e39b573cSmrg	SPS.printer_newline = True;
845e39b573cSmrg	SPS.print_attributes = attrs;
846e39b573cSmrg	SPS.print_everything = opts;
847e39b573cSmrg	xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
848e39b573cSmrg
849e39b573cSmrg	umask(save_umask);
850e39b573cSmrg	screen->printer_state = save_state;
851ad37e533Smrg	free(my_filename);
852e39b573cSmrg    }
853e39b573cSmrg}
854e39b573cSmrg
855e39b573cSmrgvoid
856e39b573cSmrgxtermPrintOnXError(XtermWidget xw, int n)
857e39b573cSmrg{
858e39b573cSmrg#if OPT_PRINT_ON_EXIT
859e39b573cSmrg    /*
860e39b573cSmrg     * The user may have requested that the contents of the screen will be
861e39b573cSmrg     * written to a file if an X error occurs.
862e39b573cSmrg     */
863e39b573cSmrg    if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) {
864e39b573cSmrg	Boolean printIt = False;
865e39b573cSmrg
866e39b573cSmrg	switch (n) {
867e39b573cSmrg	case ERROR_XERROR:
868e39b573cSmrg	    /* FALLTHRU */
869e39b573cSmrg	case ERROR_XIOERROR:
870e39b573cSmrg	    /* FALLTHRU */
871e39b573cSmrg	case ERROR_ICEERROR:
872e39b573cSmrg	    printIt = True;
873e39b573cSmrg	    break;
874e39b573cSmrg	}
875e39b573cSmrg
876e39b573cSmrg	if (printIt) {
877e39b573cSmrg	    xtermPrintImmediately(xw,
878e39b573cSmrg				  resource.printFileOnXError,
879e39b573cSmrg				  resource.printOptsOnXError,
880e39b573cSmrg				  resource.printModeOnXError);
881e39b573cSmrg	}
882e39b573cSmrg    }
883e39b573cSmrg#else
884e39b573cSmrg    (void) xw;
885e39b573cSmrg    (void) n;
886e39b573cSmrg#endif
887e39b573cSmrg}
888