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