print.c revision e39b573c
1e39b573cSmrg/* $XTermId: print.c,v 1.138 2011/07/14 23:49:10 tom Exp $ */
2d522f475Smrg
3d522f475Smrg/************************************************************
4d522f475Smrg
5a1f3da82SmrgCopyright 1997-2010,2011 by Thomas E. Dickey
6d522f475Smrg
7d522f475Smrg                        All Rights Reserved
8d522f475Smrg
9d522f475SmrgPermission is hereby granted, free of charge, to any person obtaining a
10d522f475Smrgcopy of this software and associated documentation files (the
11d522f475Smrg"Software"), to deal in the Software without restriction, including
12d522f475Smrgwithout limitation the rights to use, copy, modify, merge, publish,
13d522f475Smrgdistribute, sublicense, and/or sell copies of the Software, and to
14d522f475Smrgpermit persons to whom the Software is furnished to do so, subject to
15d522f475Smrgthe following conditions:
16d522f475Smrg
17d522f475SmrgThe above copyright notice and this permission notice shall be included
18d522f475Smrgin all copies or substantial portions of the Software.
19d522f475Smrg
20d522f475SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21d522f475SmrgOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22d522f475SmrgMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23d522f475SmrgIN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
24d522f475SmrgCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25d522f475SmrgTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26d522f475SmrgSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27d522f475Smrg
28d522f475SmrgExcept as contained in this notice, the name(s) of the above copyright
29d522f475Smrgholders shall not be used in advertising or otherwise to promote the
30d522f475Smrgsale, use or other dealings in this Software without prior written
31d522f475Smrgauthorization.
32d522f475Smrg
33d522f475Smrg********************************************************/
34d522f475Smrg
35d522f475Smrg#include <xterm.h>
36d522f475Smrg#include <data.h>
37d522f475Smrg#include <menu.h>
38d522f475Smrg#include <error.h>
3920d2c4d2Smrg#include <xstrings.h>
40d522f475Smrg
41d522f475Smrg#include <stdio.h>
42e39b573cSmrg#include <sys/stat.h>
43d522f475Smrg
44d522f475Smrg#undef  CTRL
45d522f475Smrg#define	CTRL(c)	((c) & 0x1f)
46d522f475Smrg
47d522f475Smrg#define SHIFT_IN  '\017'
48d522f475Smrg#define SHIFT_OUT '\016'
49d522f475Smrg
50d522f475Smrg#define CSET_IN   'A'
51d522f475Smrg#define CSET_OUT  '0'
52d522f475Smrg
53d522f475Smrg#define isForm(c)      ((c) == '\r' || (c) == '\n' || (c) == '\f')
54e39b573cSmrg#define Strlen(a)      strlen((const char *)a)
55e39b573cSmrg#define Strcmp(a,b)    strcmp((const char *)a,(const char *)b)
56e39b573cSmrg#define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c)
57e39b573cSmrg
58e39b573cSmrg#define SPS PrinterOf(screen)
59d522f475Smrg
60d522f475Smrg#ifdef VMS
61d522f475Smrg#define VMS_TEMP_PRINT_FILE "sys$scratch:xterm_print.txt"
62d522f475Smrg#endif
63d522f475Smrg
64956cc18dSsnjstatic void charToPrinter(XtermWidget /* xw */ ,
65956cc18dSsnj			  unsigned /* chr */ );
66956cc18dSsnjstatic void printLine(XtermWidget /* xw */ ,
67956cc18dSsnj		      int /* row */ ,
6820d2c4d2Smrg		      unsigned /* chr */ ,
6920d2c4d2Smrg		      PrinterFlags * /* p */ );
70956cc18dSsnjstatic void send_CharSet(XtermWidget /* xw */ ,
71956cc18dSsnj			 LineData * /* ld */ );
72956cc18dSsnjstatic void send_SGR(XtermWidget /* xw */ ,
73956cc18dSsnj		     unsigned /* attr */ ,
74956cc18dSsnj		     unsigned /* fg */ ,
75956cc18dSsnj		     unsigned /* bg */ );
76956cc18dSsnjstatic void stringToPrinter(XtermWidget /* xw */ ,
7720d2c4d2Smrg			    const char * /*str */ );
78d522f475Smrg
79e39b573cSmrgvoid
80956cc18dSsnjclosePrinter(XtermWidget xw GCC_UNUSED)
81d522f475Smrg{
82956cc18dSsnj    if (xtermHasPrinter(xw) != 0) {
83956cc18dSsnj	TScreen *screen = TScreenOf(xw);
84e39b573cSmrg#ifdef VMS
85d522f475Smrg	char pcommand[256];
86d522f475Smrg	(void) sprintf(pcommand, "%s %s;",
87e39b573cSmrg		       SPS.printer_command,
88d522f475Smrg		       VMS_TEMP_PRINT_FILE);
89d522f475Smrg#endif
90d522f475Smrg
91e39b573cSmrg	if (SPS.fp != 0) {
92e39b573cSmrg	    fclose(SPS.fp);
93d522f475Smrg	    TRACE(("closed printer, waiting...\n"));
94d522f475Smrg#ifdef VMS			/* This is a quick hack, really should use
95d522f475Smrg				   spawn and check status or system services
96d522f475Smrg				   and go straight to the queue */
97d522f475Smrg	    (void) system(pcommand);
98d522f475Smrg#else /* VMS */
99d522f475Smrg	    while (nonblocking_wait() > 0)
100d522f475Smrg#endif /* VMS */
101d522f475Smrg		;
102e39b573cSmrg	    SPS.fp = 0;
103e39b573cSmrg	    SPS.isOpen = False;
104d522f475Smrg	    TRACE(("closed printer\n"));
105d522f475Smrg	}
106d522f475Smrg    }
107d522f475Smrg}
108d522f475Smrg
109d522f475Smrgstatic void
110956cc18dSsnjprintCursorLine(XtermWidget xw)
111d522f475Smrg{
112956cc18dSsnj    TScreen *screen = TScreenOf(xw);
113d522f475Smrg
114d522f475Smrg    TRACE(("printCursorLine\n"));
11520d2c4d2Smrg    printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, 0));
116d522f475Smrg}
117d522f475Smrg
118d522f475Smrg#define NO_COLOR	((unsigned)-1)
119d522f475Smrg
120d522f475Smrg/*
121d522f475Smrg * DEC's manual doesn't document whether trailing blanks are removed, or what
122d522f475Smrg * happens with a line that is entirely blank.  This function prints the
123d522f475Smrg * characters that xterm would allow as a selection (which may include blanks).
124d522f475Smrg */
125d522f475Smrgstatic void
12620d2c4d2SmrgprintLine(XtermWidget xw, int row, unsigned chr, PrinterFlags * p)
127d522f475Smrg{
128956cc18dSsnj    TScreen *screen = TScreenOf(xw);
129d522f475Smrg    int inx = ROW2INX(screen, row);
130956cc18dSsnj    LineData *ld;
131d522f475Smrg    Char attr = 0;
132d522f475Smrg    unsigned ch;
133d522f475Smrg    int last = MaxCols(screen);
134d522f475Smrg    int col;
135d522f475Smrg#if OPT_ISO_COLORS && OPT_PRINT_COLORS
136956cc18dSsnj#define ColorOf(ld,col) (ld->color[col])
137d522f475Smrg#endif
138d522f475Smrg    unsigned fg = NO_COLOR, last_fg = NO_COLOR;
139d522f475Smrg    unsigned bg = NO_COLOR, last_bg = NO_COLOR;
140d522f475Smrg    int cs = CSET_IN;
141d522f475Smrg    int last_cs = CSET_IN;
142d522f475Smrg
143956cc18dSsnj    ld = getLineData(screen, inx);
14420d2c4d2Smrg    if (ld == 0)
14520d2c4d2Smrg	return;
14620d2c4d2Smrg
147d522f475Smrg    TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n",
148d522f475Smrg	   row, ROW2INX(screen, row), screen->topline, screen->max_row, chr,
149956cc18dSsnj	   visibleIChars(ld->charData, (unsigned) last)));
150956cc18dSsnj
151d522f475Smrg    while (last > 0) {
152956cc18dSsnj	if ((ld->attribs[last - 1] & CHARDRAWN) == 0)
153d522f475Smrg	    last--;
154d522f475Smrg	else
155d522f475Smrg	    break;
156d522f475Smrg    }
157d522f475Smrg    if (last) {
15820d2c4d2Smrg	if (p->print_attributes) {
159956cc18dSsnj	    send_CharSet(xw, ld);
160956cc18dSsnj	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
161d522f475Smrg	}
162d522f475Smrg	for (col = 0; col < last; col++) {
163956cc18dSsnj	    ch = ld->charData[col];
164d522f475Smrg#if OPT_PRINT_COLORS
165d522f475Smrg	    if (screen->colorMode) {
16620d2c4d2Smrg		if (p->print_attributes > 1) {
167956cc18dSsnj		    fg = (ld->attribs[col] & FG_COLOR)
168956cc18dSsnj			? extract_fg(xw, ColorOf(ld, col), ld->attribs[col])
169d522f475Smrg			: NO_COLOR;
170956cc18dSsnj		    bg = (ld->attribs[col] & BG_COLOR)
171956cc18dSsnj			? extract_bg(xw, ColorOf(ld, col), ld->attribs[col])
172d522f475Smrg			: NO_COLOR;
173d522f475Smrg		}
174d522f475Smrg	    }
175d522f475Smrg#endif
176956cc18dSsnj	    if ((((ld->attribs[col] & SGR_MASK) != attr)
177d522f475Smrg#if OPT_PRINT_COLORS
178d522f475Smrg		 || (last_fg != fg) || (last_bg != bg)
179d522f475Smrg#endif
180d522f475Smrg		)
181d522f475Smrg		&& ch) {
182956cc18dSsnj		attr = CharOf(ld->attribs[col] & SGR_MASK);
18320d2c4d2Smrg#if OPT_PRINT_COLORS
184d522f475Smrg		last_fg = fg;
185d522f475Smrg		last_bg = bg;
18620d2c4d2Smrg#endif
18720d2c4d2Smrg		if (p->print_attributes)
188956cc18dSsnj		    send_SGR(xw, attr, fg, bg);
189d522f475Smrg	    }
190d522f475Smrg
191d522f475Smrg	    if (ch == 0)
192d522f475Smrg		ch = ' ';
193d522f475Smrg
194d522f475Smrg#if OPT_WIDE_CHARS
195d522f475Smrg	    if (screen->utf8_mode)
196d522f475Smrg		cs = CSET_IN;
197d522f475Smrg	    else
198d522f475Smrg#endif
199d522f475Smrg		cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT;
200d522f475Smrg	    if (last_cs != cs) {
20120d2c4d2Smrg		if (p->print_attributes) {
202956cc18dSsnj		    charToPrinter(xw,
203956cc18dSsnj				  (unsigned) ((cs == CSET_OUT)
204d522f475Smrg					      ? SHIFT_OUT
205d522f475Smrg					      : SHIFT_IN));
206d522f475Smrg		}
207d522f475Smrg		last_cs = cs;
208d522f475Smrg	    }
209d522f475Smrg
210d522f475Smrg	    /* FIXME:  we shouldn't have to map back from the
211d522f475Smrg	     * alternate character set, except that the
212d522f475Smrg	     * corresponding charset information is not encoded
213d522f475Smrg	     * into the CSETS array.
214d522f475Smrg	     */
215956cc18dSsnj	    charToPrinter(xw,
216956cc18dSsnj			  ((cs == CSET_OUT)
217d522f475Smrg			   ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f))
218d522f475Smrg			   : ch));
219d522f475Smrg	    if_OPT_WIDE_CHARS(screen, {
220956cc18dSsnj		size_t off;
221956cc18dSsnj		for_each_combData(off, ld) {
222956cc18dSsnj		    ch = ld->combData[off][col];
223956cc18dSsnj		    if (ch == 0)
224d522f475Smrg			break;
225956cc18dSsnj		    charToPrinter(xw, ch);
226d522f475Smrg		}
227d522f475Smrg	    });
228d522f475Smrg	}
22920d2c4d2Smrg	if (p->print_attributes) {
230956cc18dSsnj	    send_SGR(xw, 0, NO_COLOR, NO_COLOR);
231d522f475Smrg	    if (cs != CSET_IN)
232956cc18dSsnj		charToPrinter(xw, SHIFT_IN);
233d522f475Smrg	}
234d522f475Smrg    }
23520d2c4d2Smrg
23620d2c4d2Smrg    /* finish line (protocol for attributes needs a CR */
23720d2c4d2Smrg    if (p->print_attributes)
238956cc18dSsnj	charToPrinter(xw, '\r');
23920d2c4d2Smrg
24020d2c4d2Smrg    if (chr && !(p->printer_newline)) {
24120d2c4d2Smrg	if (LineTstWrapped(ld))
24220d2c4d2Smrg	    chr = '\0';
24320d2c4d2Smrg    }
24420d2c4d2Smrg
24520d2c4d2Smrg    if (chr)
24620d2c4d2Smrg	charToPrinter(xw, chr);
247956cc18dSsnj
248956cc18dSsnj    return;
249d522f475Smrg}
250d522f475Smrg
25120d2c4d2Smrg#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0')
25220d2c4d2Smrg
253e39b573cSmrgstatic void
254e39b573cSmrgprintLines(XtermWidget xw, int top, int bot, PrinterFlags * p)
255e39b573cSmrg{
256e39b573cSmrg    TRACE(("printLines, rows %d..%d\n", top, bot));
257e39b573cSmrg    while (top <= bot) {
258e39b573cSmrg	printLine(xw, top, PrintNewLine(), p);
259e39b573cSmrg	++top;
260e39b573cSmrg    }
261e39b573cSmrg}
262e39b573cSmrg
263d522f475Smrgvoid
26420d2c4d2SmrgxtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags * p)
265d522f475Smrg{
266956cc18dSsnj    if (XtIsRealized((Widget) xw)) {
267956cc18dSsnj	TScreen *screen = TScreenOf(xw);
26820d2c4d2Smrg	Bool extent = (use_DECPEX && p->printer_extent);
269e39b573cSmrg	Boolean was_open = SPS.isOpen;
270d522f475Smrg
271e39b573cSmrg	printLines(xw,
272e39b573cSmrg		   extent ? 0 : screen->top_marg,
273e39b573cSmrg		   extent ? screen->max_row : screen->bot_marg,
274e39b573cSmrg		   p);
27520d2c4d2Smrg	if (p->printer_formfeed)
276956cc18dSsnj	    charToPrinter(xw, '\f');
277d522f475Smrg
278e39b573cSmrg	if (!was_open || SPS.printer_autoclose) {
279956cc18dSsnj	    closePrinter(xw);
280d522f475Smrg	}
281d522f475Smrg    } else {
28220d2c4d2Smrg	Bell(xw, XkbBI_MinorError, 0);
283d522f475Smrg    }
284d522f475Smrg}
285d522f475Smrg
286d522f475Smrg/*
287e39b573cSmrg * If p->print_everything is zero, use this behavior:
288d522f475Smrg * If the alternate screen is active, we'll print only that.  Otherwise, print
289d522f475Smrg * the normal screen plus all scrolled-back lines.  The distinction is made
290d522f475Smrg * because the normal screen's buffer is part of the overall scrollback buffer.
291e39b573cSmrg *
292e39b573cSmrg * Otherwise, decode bits:
293e39b573cSmrg *	1 = current screen
294e39b573cSmrg *	2 = normal screen
295e39b573cSmrg *	4 = alternate screen
296e39b573cSmrg *	8 = saved lines
297d522f475Smrg */
298956cc18dSsnjvoid
29920d2c4d2SmrgxtermPrintEverything(XtermWidget xw, PrinterFlags * p)
300d522f475Smrg{
301956cc18dSsnj    TScreen *screen = TScreenOf(xw);
302e39b573cSmrg    Boolean was_open = SPS.isOpen;
303e39b573cSmrg    int save_which = screen->whichBuf;
304e39b573cSmrg    int done_which = 0;
305d522f475Smrg
306e39b573cSmrg    if (p->print_everything) {
307e39b573cSmrg	if (p->print_everything & 8) {
308e39b573cSmrg	    printLines(xw, -screen->savedlines, -(screen->topline + 1), p);
309e39b573cSmrg	}
310e39b573cSmrg	if (p->print_everything & 4) {
311e39b573cSmrg	    screen->whichBuf = 1;
312e39b573cSmrg	    done_which |= 2;
313e39b573cSmrg	    printLines(xw, 0, screen->max_row, p);
314e39b573cSmrg	    screen->whichBuf = save_which;
315e39b573cSmrg	}
316e39b573cSmrg	if (p->print_everything & 2) {
317e39b573cSmrg	    screen->whichBuf = 0;
318e39b573cSmrg	    done_which |= 1;
319e39b573cSmrg	    printLines(xw, 0, screen->max_row, p);
320e39b573cSmrg	    screen->whichBuf = save_which;
321e39b573cSmrg	}
322e39b573cSmrg	if (p->print_everything & 1) {
323e39b573cSmrg	    if (!(done_which & (1 << screen->whichBuf))) {
324e39b573cSmrg		printLines(xw, 0, screen->max_row, p);
325e39b573cSmrg	    }
326e39b573cSmrg	}
327e39b573cSmrg    } else {
328e39b573cSmrg	int top = 0;
329e39b573cSmrg	int bot = screen->max_row;
330e39b573cSmrg	if (!screen->whichBuf) {
331e39b573cSmrg	    top = -screen->savedlines - screen->topline;
332e39b573cSmrg	    bot -= screen->topline;
333e39b573cSmrg	}
334e39b573cSmrg	printLines(xw, top, bot, p);
33520d2c4d2Smrg    }
33620d2c4d2Smrg    if (p->printer_formfeed)
337956cc18dSsnj	charToPrinter(xw, '\f');
338d522f475Smrg
339e39b573cSmrg    if (!was_open || SPS.printer_autoclose) {
340956cc18dSsnj	closePrinter(xw);
341d522f475Smrg    }
342d522f475Smrg}
343d522f475Smrg
344d522f475Smrgstatic void
345956cc18dSsnjsend_CharSet(XtermWidget xw, LineData * ld)
346d522f475Smrg{
347d522f475Smrg#if OPT_DEC_CHRSET
34820d2c4d2Smrg    const char *msg = 0;
349d522f475Smrg
350956cc18dSsnj    switch (GetLineDblCS(ld)) {
351d522f475Smrg    case CSET_SWL:
352d522f475Smrg	msg = "\033#5";
353d522f475Smrg	break;
354d522f475Smrg    case CSET_DHL_TOP:
355d522f475Smrg	msg = "\033#3";
356d522f475Smrg	break;
357d522f475Smrg    case CSET_DHL_BOT:
358d522f475Smrg	msg = "\033#4";
359d522f475Smrg	break;
360d522f475Smrg    case CSET_DWL:
361d522f475Smrg	msg = "\033#6";
362d522f475Smrg	break;
363d522f475Smrg    }
364d522f475Smrg    if (msg != 0)
365956cc18dSsnj	stringToPrinter(xw, msg);
366d522f475Smrg#else
367956cc18dSsnj    (void) xw;
368956cc18dSsnj    (void) ld;
369d522f475Smrg#endif /* OPT_DEC_CHRSET */
370d522f475Smrg}
371d522f475Smrg
372d522f475Smrgstatic void
373956cc18dSsnjsend_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg)
374d522f475Smrg{
375d522f475Smrg    char msg[80];
376e39b573cSmrg
377d522f475Smrg    strcpy(msg, "\033[0");
378d522f475Smrg    if (attr & BOLD)
379d522f475Smrg	strcat(msg, ";1");
380d522f475Smrg    if (attr & UNDERLINE)
381d522f475Smrg	strcat(msg, ";4");	/* typo? DEC documents this as '2' */
382d522f475Smrg    if (attr & BLINK)
383d522f475Smrg	strcat(msg, ";5");
384d522f475Smrg    if (attr & INVERSE)		/* typo? DEC documents this as invisible */
385d522f475Smrg	strcat(msg, ";7");
386d522f475Smrg#if OPT_PRINT_COLORS
387d522f475Smrg    if (bg != NO_COLOR) {
388d522f475Smrg	sprintf(msg + strlen(msg), ";%u", (bg < 8) ? (40 + bg) : (92 + bg));
389d522f475Smrg    }
390d522f475Smrg    if (fg != NO_COLOR) {
391d522f475Smrg#if OPT_PC_COLORS
39220d2c4d2Smrg	if (TScreenOf(xw)->boldColors
393d522f475Smrg	    && fg > 8
394d522f475Smrg	    && (attr & BOLD) != 0)
395d522f475Smrg	    fg -= 8;
396d522f475Smrg#endif
397d522f475Smrg	sprintf(msg + strlen(msg), ";%u", (fg < 8) ? (30 + fg) : (82 + fg));
398d522f475Smrg    }
399d522f475Smrg#else
400d522f475Smrg    (void) bg;
401d522f475Smrg    (void) fg;
402d522f475Smrg#endif
403d522f475Smrg    strcat(msg, "m");
404956cc18dSsnj    stringToPrinter(xw, msg);
405d522f475Smrg}
406d522f475Smrg
407d522f475Smrg/*
408d522f475Smrg * This implementation only knows how to write to a pipe.
409d522f475Smrg */
410d522f475Smrgstatic void
411956cc18dSsnjcharToPrinter(XtermWidget xw, unsigned chr)
412d522f475Smrg{
413956cc18dSsnj    TScreen *screen = TScreenOf(xw);
414d522f475Smrg
415e39b573cSmrg    if (!SPS.isOpen && xtermHasPrinter(xw)) {
416e39b573cSmrg	switch (SPS.toFile) {
417e39b573cSmrg	    /*
418e39b573cSmrg	     * write to a pipe.
419e39b573cSmrg	     */
420e39b573cSmrg	case False:
421e39b573cSmrg#ifdef VMS
422e39b573cSmrg	    /*
423e39b573cSmrg	     * This implementation only knows how to write to a file.  When the
424e39b573cSmrg	     * file is closed the print command executes.  Print command must
425e39b573cSmrg	     * be of the form:
426e39b573cSmrg	     *   print/que=name/delete [/otherflags].
427e39b573cSmrg	     */
428e39b573cSmrg	    SPS.fp = fopen(VMS_TEMP_PRINT_FILE, "w");
429d522f475Smrg#else
430e39b573cSmrg	    {
431e39b573cSmrg		FILE *input;
432e39b573cSmrg		int my_pipe[2];
433e39b573cSmrg		int c;
434e39b573cSmrg		pid_t my_pid;
435e39b573cSmrg
436e39b573cSmrg		if (pipe(my_pipe))
437e39b573cSmrg		    SysError(ERROR_FORK);
438e39b573cSmrg		if ((my_pid = fork()) < 0)
439e39b573cSmrg		    SysError(ERROR_FORK);
440e39b573cSmrg
441e39b573cSmrg		if (my_pid == 0) {
442e39b573cSmrg		    TRACE_CLOSE();
443e39b573cSmrg		    close(my_pipe[1]);	/* printer is silent */
444e39b573cSmrg		    close(screen->respond);
445e39b573cSmrg
446e39b573cSmrg		    close(fileno(stdout));
447e39b573cSmrg		    dup2(fileno(stderr), 1);
448e39b573cSmrg
449e39b573cSmrg		    if (fileno(stderr) != 2) {
450e39b573cSmrg			dup2(fileno(stderr), 2);
451e39b573cSmrg			close(fileno(stderr));
452e39b573cSmrg		    }
453e39b573cSmrg
454e39b573cSmrg		    /* don't want privileges! */
455e39b573cSmrg		    if (xtermResetIds(screen) < 0)
456e39b573cSmrg			exit(1);
457e39b573cSmrg
458e39b573cSmrg		    SPS.fp = popen(SPS.printer_command, "w");
459e39b573cSmrg		    input = fdopen(my_pipe[0], "r");
460e39b573cSmrg		    while ((c = fgetc(input)) != EOF) {
461e39b573cSmrg			fputc(c, SPS.fp);
462e39b573cSmrg			if (isForm(c))
463e39b573cSmrg			    fflush(SPS.fp);
464e39b573cSmrg		    }
465e39b573cSmrg		    pclose(SPS.fp);
466e39b573cSmrg		    exit(0);
467e39b573cSmrg		} else {
468e39b573cSmrg		    close(my_pipe[0]);	/* won't read from printer */
469e39b573cSmrg		    SPS.fp = fdopen(my_pipe[1], "w");
470e39b573cSmrg		    TRACE(("opened printer from pid %d/%d\n",
471e39b573cSmrg			   (int) getpid(), (int) my_pid));
472e39b573cSmrg		}
473d522f475Smrg	    }
474d522f475Smrg#endif
475e39b573cSmrg	    break;
476e39b573cSmrg	case True:
477e39b573cSmrg	    TRACE(("opening \"%s\" as printer output\n", SPS.printer_command));
478e39b573cSmrg	    SPS.fp = fopen(SPS.printer_command, "w");
479e39b573cSmrg	    break;
480e39b573cSmrg	}
481e39b573cSmrg	SPS.isOpen = True;
482d522f475Smrg    }
483e39b573cSmrg    if (SPS.fp != 0) {
484d522f475Smrg#if OPT_WIDE_CHARS
485d522f475Smrg	if (chr > 127) {
486d522f475Smrg	    Char temp[10];
487d522f475Smrg	    *convertToUTF8(temp, chr) = 0;
488e39b573cSmrg	    fputs((char *) temp, SPS.fp);
489d522f475Smrg	} else
490d522f475Smrg#endif
491e39b573cSmrg	    fputc((int) chr, SPS.fp);
492d522f475Smrg	if (isForm(chr))
493e39b573cSmrg	    fflush(SPS.fp);
494d522f475Smrg    }
495d522f475Smrg}
496d522f475Smrg
497d522f475Smrgstatic void
49820d2c4d2SmrgstringToPrinter(XtermWidget xw, const char *str)
499d522f475Smrg{
500d522f475Smrg    while (*str)
501956cc18dSsnj	charToPrinter(xw, CharOf(*str++));
502d522f475Smrg}
503d522f475Smrg
504d522f475Smrg/*
505d522f475Smrg * This module implements the MC (Media Copy) and related printing control
506d522f475Smrg * sequences for VTxxx emulation.  This is based on the description in the
507d522f475Smrg * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment
508d522f475Smrg * Corp., March 1987).
509d522f475Smrg */
510d522f475Smrgvoid
511956cc18dSsnjxtermMediaControl(XtermWidget xw, int param, int private_seq)
512d522f475Smrg{
513d522f475Smrg    TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq));
514d522f475Smrg
515d522f475Smrg    if (private_seq) {
516d522f475Smrg	switch (param) {
517d522f475Smrg	case 1:
518956cc18dSsnj	    printCursorLine(xw);
519d522f475Smrg	    break;
520d522f475Smrg	case 4:
521956cc18dSsnj	    setPrinterControlMode(xw, 0);
522d522f475Smrg	    break;
523d522f475Smrg	case 5:
524956cc18dSsnj	    setPrinterControlMode(xw, 1);
525d522f475Smrg	    break;
526d522f475Smrg	case 10:		/* VT320 */
52720d2c4d2Smrg	    xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, 0));
528d522f475Smrg	    break;
529d522f475Smrg	case 11:		/* VT320 */
53020d2c4d2Smrg	    xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
531d522f475Smrg	    break;
532d522f475Smrg	}
533d522f475Smrg    } else {
534d522f475Smrg	switch (param) {
535d522f475Smrg	case -1:
536d522f475Smrg	case 0:
53720d2c4d2Smrg	    xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, 0));
538d522f475Smrg	    break;
539d522f475Smrg	case 4:
540956cc18dSsnj	    setPrinterControlMode(xw, 0);
541d522f475Smrg	    break;
542d522f475Smrg	case 5:
543956cc18dSsnj	    setPrinterControlMode(xw, 2);
544d522f475Smrg	    break;
545d522f475Smrg	}
546d522f475Smrg    }
547d522f475Smrg}
548d522f475Smrg
549d522f475Smrg/*
550d522f475Smrg * When in autoprint mode, the printer prints a line from the screen when you
551d522f475Smrg * move the cursor off that line with an LF, FF, or VT character, or an
552d522f475Smrg * autowrap occurs.  The printed line ends with a CR and the character (LF, FF
553d522f475Smrg * or VT) that moved the cursor off the previous line.
554d522f475Smrg */
555d522f475Smrgvoid
556956cc18dSsnjxtermAutoPrint(XtermWidget xw, unsigned chr)
557d522f475Smrg{
558956cc18dSsnj    TScreen *screen = TScreenOf(xw);
559d522f475Smrg
560e39b573cSmrg    if (SPS.printer_controlmode == 1) {
561d522f475Smrg	TRACE(("AutoPrint %d\n", chr));
56220d2c4d2Smrg	printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, 0));
563e39b573cSmrg	if (SPS.fp != 0)
564e39b573cSmrg	    fflush(SPS.fp);
565d522f475Smrg    }
566d522f475Smrg}
567d522f475Smrg
568d522f475Smrg/*
569d522f475Smrg * When in printer controller mode, the terminal sends received characters to
570d522f475Smrg * the printer without displaying them on the screen. The terminal sends all
571d522f475Smrg * characters and control sequences to the printer, except NUL, XON, XOFF, and
572d522f475Smrg * the printer controller sequences.
573d522f475Smrg *
574d522f475Smrg * This function eats characters, returning 0 as long as it must buffer or
575d522f475Smrg * divert to the printer.  We're only invoked here when in printer controller
576d522f475Smrg * mode, and handle the exit from that mode.
577d522f475Smrg */
578d522f475Smrg#define LB '['
579d522f475Smrg
580d522f475Smrgint
581956cc18dSsnjxtermPrinterControl(XtermWidget xw, int chr)
582d522f475Smrg{
583956cc18dSsnj    TScreen *screen = TScreenOf(xw);
584d522f475Smrg    /* *INDENT-OFF* */
585e39b573cSmrg    static const struct {
586e39b573cSmrg	const Char seq[5];
587d522f475Smrg	int active;
588d522f475Smrg    } tbl[] = {
589d522f475Smrg	{ { ANSI_CSI, '5', 'i'      }, 2 },
590d522f475Smrg	{ { ANSI_CSI, '4', 'i'      }, 0 },
591d522f475Smrg	{ { ANSI_ESC, LB,  '5', 'i' }, 2 },
592d522f475Smrg	{ { ANSI_ESC, LB,  '4', 'i' }, 0 },
593d522f475Smrg    };
594d522f475Smrg    /* *INDENT-ON* */
595d522f475Smrg
596d522f475Smrg    static Char bfr[10];
597d522f475Smrg    static size_t length;
598d522f475Smrg    size_t n;
599d522f475Smrg
600d522f475Smrg    TRACE(("In printer:%04X\n", chr));
601d522f475Smrg
602d522f475Smrg    switch (chr) {
603d522f475Smrg    case 0:
604d522f475Smrg    case CTRL('Q'):
605d522f475Smrg    case CTRL('S'):
606d522f475Smrg	return 0;		/* ignored by application */
607d522f475Smrg
608d522f475Smrg    case ANSI_CSI:
609d522f475Smrg    case ANSI_ESC:
610d522f475Smrg    case '[':
611d522f475Smrg    case '4':
612d522f475Smrg    case '5':
613d522f475Smrg    case 'i':
6144e40088cSchristos	bfr[length++] = CharOf(chr);
615d522f475Smrg	for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) {
616d522f475Smrg	    size_t len = Strlen(tbl[n].seq);
617d522f475Smrg
618d522f475Smrg	    if (length == len
619d522f475Smrg		&& Strcmp(bfr, tbl[n].seq) == 0) {
620956cc18dSsnj		setPrinterControlMode(xw, tbl[n].active);
621e39b573cSmrg		if (SPS.printer_autoclose
622e39b573cSmrg		    && SPS.printer_controlmode == 0)
623956cc18dSsnj		    closePrinter(xw);
624d522f475Smrg		length = 0;
625d522f475Smrg		return 0;
626d522f475Smrg	    } else if (len > length
627d522f475Smrg		       && Strncmp(bfr, tbl[n].seq, length) == 0) {
628d522f475Smrg		return 0;
629d522f475Smrg	    }
630d522f475Smrg	}
631d522f475Smrg	length--;
632d522f475Smrg
633d522f475Smrg	/* FALLTHRU */
634d522f475Smrg
635d522f475Smrg    default:
636d522f475Smrg	for (n = 0; n < length; n++)
637956cc18dSsnj	    charToPrinter(xw, bfr[n]);
6384e40088cSchristos	bfr[0] = CharOf(chr);
639d522f475Smrg	length = 1;
640d522f475Smrg	return 0;
641d522f475Smrg    }
642d522f475Smrg}
643d522f475Smrg
644d522f475Smrg/*
645d522f475Smrg * If there is no printer command, we will ignore printer controls.
646d522f475Smrg */
647d522f475SmrgBool
648956cc18dSsnjxtermHasPrinter(XtermWidget xw)
649d522f475Smrg{
650956cc18dSsnj    TScreen *screen = TScreenOf(xw);
651d522f475Smrg
652e39b573cSmrg    return (strlen(SPS.printer_command) != 0);
653d522f475Smrg}
654d522f475Smrg
655d522f475Smrg#define showPrinterControlMode(mode) \
656d522f475Smrg		(((mode) == 0) \
657d522f475Smrg		 ? "normal" \
658d522f475Smrg		 : ((mode) == 1 \
659d522f475Smrg		    ? "autoprint" \
660d522f475Smrg		    : "printer controller"))
661d522f475Smrg
662d522f475Smrgvoid
663956cc18dSsnjsetPrinterControlMode(XtermWidget xw, int mode)
664d522f475Smrg{
66520d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
66620d2c4d2Smrg
667956cc18dSsnj    if (xtermHasPrinter(xw)
668e39b573cSmrg	&& SPS.printer_controlmode != mode) {
669d522f475Smrg	TRACE(("%s %s mode\n",
670d522f475Smrg	       (mode
671d522f475Smrg		? "set"
672d522f475Smrg		: "reset"),
673d522f475Smrg	       (mode
674d522f475Smrg		? showPrinterControlMode(mode)
675e39b573cSmrg		: showPrinterControlMode(SPS.printer_controlmode))));
676e39b573cSmrg	SPS.printer_controlmode = mode;
677d522f475Smrg	update_print_redir();
678d522f475Smrg    }
679d522f475Smrg}
68020d2c4d2Smrg
68120d2c4d2SmrgPrinterFlags *
68220d2c4d2SmrggetPrinterFlags(XtermWidget xw, String * params, Cardinal *param_count)
68320d2c4d2Smrg{
68420d2c4d2Smrg    /* *INDENT-OFF* */
68520d2c4d2Smrg    static const struct {
68620d2c4d2Smrg	const char *name;
68720d2c4d2Smrg	unsigned    offset;
68820d2c4d2Smrg	int	    value;
68920d2c4d2Smrg    } table[] = {
69020d2c4d2Smrg	{ "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 },
69120d2c4d2Smrg	{ "FormFeed",	XtOffsetOf(PrinterFlags, printer_formfeed), 1 },
69220d2c4d2Smrg	{ "noNewLine",	XtOffsetOf(PrinterFlags, printer_newline),  0 },
69320d2c4d2Smrg	{ "NewLine",	XtOffsetOf(PrinterFlags, printer_newline),  1 },
69420d2c4d2Smrg	{ "noAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 0 },
69520d2c4d2Smrg	{ "monoAttrs",	XtOffsetOf(PrinterFlags, print_attributes), 1 },
69620d2c4d2Smrg	{ "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 },
69720d2c4d2Smrg    };
69820d2c4d2Smrg    /* *INDENT-ON* */
69920d2c4d2Smrg
70020d2c4d2Smrg    TScreen *screen = TScreenOf(xw);
70120d2c4d2Smrg    PrinterFlags *result = &(screen->printer_flags);
70220d2c4d2Smrg
70320d2c4d2Smrg    TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0));
70420d2c4d2Smrg
705e39b573cSmrg    result->printer_extent = SPS.printer_extent;
706e39b573cSmrg    result->printer_formfeed = SPS.printer_formfeed;
707e39b573cSmrg    result->printer_newline = SPS.printer_newline;
708e39b573cSmrg    result->print_attributes = SPS.print_attributes;
709e39b573cSmrg    result->print_everything = SPS.print_everything;
71020d2c4d2Smrg
71120d2c4d2Smrg    if (param_count != 0 && *param_count != 0) {
71220d2c4d2Smrg	Cardinal j;
71320d2c4d2Smrg	unsigned k;
71420d2c4d2Smrg	for (j = 0; j < *param_count; ++j) {
71520d2c4d2Smrg	    TRACE(("param%d:%s\n", j, params[j]));
71620d2c4d2Smrg	    for (k = 0; k < XtNumber(table); ++k) {
71720d2c4d2Smrg		if (!x_strcasecmp(params[j], table[k].name)) {
718a1f3da82Smrg		    int *ptr = (int *) (void *) ((char *) result + table[k].offset);
71920d2c4d2Smrg		    TRACE(("...PrinterFlags(%s) %d->%d\n",
72020d2c4d2Smrg			   table[k].name,
72120d2c4d2Smrg			   *ptr,
72220d2c4d2Smrg			   table[k].value));
72320d2c4d2Smrg		    *ptr = table[k].value;
72420d2c4d2Smrg		    break;
72520d2c4d2Smrg		}
72620d2c4d2Smrg	    }
72720d2c4d2Smrg	}
72820d2c4d2Smrg    }
72920d2c4d2Smrg
73020d2c4d2Smrg    return result;
73120d2c4d2Smrg}
732e39b573cSmrg
733e39b573cSmrg/*
734e39b573cSmrg * Print a timestamped copy of everything.
735e39b573cSmrg */
736e39b573cSmrgvoid
737e39b573cSmrgxtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs)
738e39b573cSmrg{
739e39b573cSmrg    TScreen *screen = TScreenOf(xw);
740e39b573cSmrg    PrinterState save_state = screen->printer_state;
741e39b573cSmrg    char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename));
742e39b573cSmrg
743e39b573cSmrg    if (my_filename != 0) {
744e39b573cSmrg	unsigned save_umask = umask(0177);
745e39b573cSmrg
746e39b573cSmrg	timestamp_filename(my_filename, filename);
747e39b573cSmrg	SPS.fp = 0;
748e39b573cSmrg	SPS.isOpen = False;
749e39b573cSmrg	SPS.toFile = True;
750e39b573cSmrg	SPS.printer_command = my_filename;
751e39b573cSmrg	SPS.printer_autoclose = True;
752e39b573cSmrg	SPS.printer_formfeed = False;
753e39b573cSmrg	SPS.printer_newline = True;
754e39b573cSmrg	SPS.print_attributes = attrs;
755e39b573cSmrg	SPS.print_everything = opts;
756e39b573cSmrg	xtermPrintEverything(xw, getPrinterFlags(xw, NULL, 0));
757e39b573cSmrg
758e39b573cSmrg	umask(save_umask);
759e39b573cSmrg	screen->printer_state = save_state;
760e39b573cSmrg    }
761e39b573cSmrg}
762e39b573cSmrg
763e39b573cSmrgvoid
764e39b573cSmrgxtermPrintOnXError(XtermWidget xw, int n)
765e39b573cSmrg{
766e39b573cSmrg#if OPT_PRINT_ON_EXIT
767e39b573cSmrg    /*
768e39b573cSmrg     * The user may have requested that the contents of the screen will be
769e39b573cSmrg     * written to a file if an X error occurs.
770e39b573cSmrg     */
771e39b573cSmrg    if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) {
772e39b573cSmrg	Boolean printIt = False;
773e39b573cSmrg
774e39b573cSmrg	switch (n) {
775e39b573cSmrg	case ERROR_XERROR:
776e39b573cSmrg	    /* FALLTHRU */
777e39b573cSmrg	case ERROR_XIOERROR:
778e39b573cSmrg	    /* FALLTHRU */
779e39b573cSmrg	case ERROR_ICEERROR:
780e39b573cSmrg	    printIt = True;
781e39b573cSmrg	    break;
782e39b573cSmrg	}
783e39b573cSmrg
784e39b573cSmrg	if (printIt) {
785e39b573cSmrg	    xtermPrintImmediately(xw,
786e39b573cSmrg				  resource.printFileOnXError,
787e39b573cSmrg				  resource.printOptsOnXError,
788e39b573cSmrg				  resource.printModeOnXError);
789e39b573cSmrg	}
790e39b573cSmrg    }
791e39b573cSmrg#else
792e39b573cSmrg    (void) xw;
793e39b573cSmrg    (void) n;
794e39b573cSmrg#endif
795e39b573cSmrg}
796