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