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