15104ee6eSmrg/* $XTermId: print.c,v 1.180 2025/01/05 20:36:49 tom Exp $ */ 2d522f475Smrg 30bd37d32Smrg/* 45104ee6eSmrg * Copyright 1997-2024,2025 by Thomas E. Dickey 50bd37d32Smrg * 60bd37d32Smrg * All Rights Reserved 70bd37d32Smrg * 80bd37d32Smrg * Permission is hereby granted, free of charge, to any person obtaining a 90bd37d32Smrg * copy of this software and associated documentation files (the 100bd37d32Smrg * "Software"), to deal in the Software without restriction, including 110bd37d32Smrg * without limitation the rights to use, copy, modify, merge, publish, 120bd37d32Smrg * distribute, sublicense, and/or sell copies of the Software, and to 130bd37d32Smrg * permit persons to whom the Software is furnished to do so, subject to 140bd37d32Smrg * the following conditions: 150bd37d32Smrg * 160bd37d32Smrg * The above copyright notice and this permission notice shall be included 170bd37d32Smrg * in all copies or substantial portions of the Software. 180bd37d32Smrg * 190bd37d32Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 200bd37d32Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 210bd37d32Smrg * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 220bd37d32Smrg * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 230bd37d32Smrg * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 240bd37d32Smrg * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 250bd37d32Smrg * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 260bd37d32Smrg * 270bd37d32Smrg * Except as contained in this notice, the name(s) of the above copyright 280bd37d32Smrg * holders shall not be used in advertising or otherwise to promote the 290bd37d32Smrg * sale, use or other dealings in this Software without prior written 300bd37d32Smrg * authorization. 310bd37d32Smrg */ 32d522f475Smrg 33d522f475Smrg#include <xterm.h> 34d522f475Smrg#include <data.h> 35d522f475Smrg#include <menu.h> 36d522f475Smrg#include <error.h> 3720d2c4d2Smrg#include <xstrings.h> 38d522f475Smrg 39d522f475Smrg#include <stdio.h> 40e39b573cSmrg#include <sys/stat.h> 41d522f475Smrg 42d522f475Smrg#undef CTRL 43d522f475Smrg#define CTRL(c) ((c) & 0x1f) 44d522f475Smrg 45d522f475Smrg#define SHIFT_IN '\017' 46d522f475Smrg#define SHIFT_OUT '\016' 47d522f475Smrg 48d522f475Smrg#define CSET_IN 'A' 49d522f475Smrg#define CSET_OUT '0' 50d522f475Smrg 51d522f475Smrg#define isForm(c) ((c) == '\r' || (c) == '\n' || (c) == '\f') 52e39b573cSmrg#define Strlen(a) strlen((const char *)a) 53e39b573cSmrg#define Strcmp(a,b) strcmp((const char *)a,(const char *)b) 54e39b573cSmrg#define Strncmp(a,b,c) strncmp((const char *)a,(const char *)b,c) 55e39b573cSmrg 56e39b573cSmrg#define SPS PrinterOf(screen) 57d522f475Smrg 58956cc18dSsnjstatic void charToPrinter(XtermWidget /* xw */ , 59956cc18dSsnj unsigned /* chr */ ); 60956cc18dSsnjstatic void printLine(XtermWidget /* xw */ , 61956cc18dSsnj int /* row */ , 6220d2c4d2Smrg unsigned /* chr */ , 6320d2c4d2Smrg PrinterFlags * /* p */ ); 64956cc18dSsnjstatic void send_CharSet(XtermWidget /* xw */ , 65956cc18dSsnj LineData * /* ld */ ); 66956cc18dSsnjstatic void send_SGR(XtermWidget /* xw */ , 67956cc18dSsnj unsigned /* attr */ , 68956cc18dSsnj unsigned /* fg */ , 69956cc18dSsnj unsigned /* bg */ ); 70956cc18dSsnjstatic void stringToPrinter(XtermWidget /* xw */ , 7120d2c4d2Smrg const char * /*str */ ); 72d522f475Smrg 73f2e35a3aSmrg#if OPT_PRINT_GRAPHICS 74f2e35a3aSmrgstatic void setGraphicsPrintToHost(XtermWidget /* xw */ , 75f2e35a3aSmrg int /* enabled */ ); 76f2e35a3aSmrg#else 77ad37e533Smrg#define setGraphicsPrintToHost(xw, enabled) /* nothing */ 78f2e35a3aSmrg#endif 79f2e35a3aSmrg 800bd37d32Smrgstatic void 81f2e35a3aSmrgclosePrinter(XtermWidget xw) 82d522f475Smrg{ 83f2e35a3aSmrg TScreen *screen = TScreenOf(xw); 845104ee6eSmrg if (SPS.fp != NULL) { 85f2e35a3aSmrg if (SPS.toFile) { 86f2e35a3aSmrg fclose(SPS.fp); 875104ee6eSmrg SPS.fp = NULL; 88f2e35a3aSmrg } else if (xtermHasPrinter(xw) != 0) { 89d522f475Smrg 900bd37d32Smrg DEBUG_MSG("closePrinter\n"); 910bd37d32Smrg pclose(SPS.fp); 92d522f475Smrg TRACE(("closed printer, waiting...\n")); 930bd37d32Smrg while (nonblocking_wait() > 0) { 94d522f475Smrg ; 950bd37d32Smrg } 965104ee6eSmrg SPS.fp = NULL; 97e39b573cSmrg SPS.isOpen = False; 98d522f475Smrg TRACE(("closed printer\n")); 990bd37d32Smrg DEBUG_MSG("...closePrinter (done)\n"); 100d522f475Smrg } 101d522f475Smrg } 102d522f475Smrg} 103d522f475Smrg 104d522f475Smrgstatic void 105956cc18dSsnjprintCursorLine(XtermWidget xw) 106d522f475Smrg{ 107956cc18dSsnj TScreen *screen = TScreenOf(xw); 108d522f475Smrg 109d522f475Smrg TRACE(("printCursorLine\n")); 1105104ee6eSmrg printLine(xw, screen->cur_row, '\n', getPrinterFlags(xw, NULL, NULL)); 111d522f475Smrg} 112d522f475Smrg 113d522f475Smrg/* 114d522f475Smrg * DEC's manual doesn't document whether trailing blanks are removed, or what 115d522f475Smrg * happens with a line that is entirely blank. This function prints the 116d522f475Smrg * characters that xterm would allow as a selection (which may include blanks). 117d522f475Smrg */ 118d522f475Smrgstatic void 119894e0ac8SmrgprintLine(XtermWidget xw, int row, unsigned chr, PrinterFlags *p) 120d522f475Smrg{ 121956cc18dSsnj TScreen *screen = TScreenOf(xw); 122d522f475Smrg int inx = ROW2INX(screen, row); 123956cc18dSsnj LineData *ld; 124d522f475Smrg int last = MaxCols(screen); 125d522f475Smrg#if OPT_ISO_COLORS && OPT_PRINT_COLORS 126956cc18dSsnj#define ColorOf(ld,col) (ld->color[col]) 127d522f475Smrg#endif 128f2e35a3aSmrg Pixel fg = NO_COLOR; 129f2e35a3aSmrg Pixel bg = NO_COLOR; 130913cc679Smrg#if OPT_PRINT_COLORS 131f2e35a3aSmrg Pixel last_fg = NO_COLOR; 132f2e35a3aSmrg Pixel last_bg = NO_COLOR; 133913cc679Smrg#endif 134d522f475Smrg 135956cc18dSsnj ld = getLineData(screen, inx); 1365104ee6eSmrg if (ld == NULL) 13720d2c4d2Smrg return; 13820d2c4d2Smrg 139d522f475Smrg TRACE(("printLine(row=%d/%d, top=%d:%d, chr=%d):%s\n", 140d522f475Smrg row, ROW2INX(screen, row), screen->topline, screen->max_row, chr, 141956cc18dSsnj visibleIChars(ld->charData, (unsigned) last))); 142956cc18dSsnj 143d522f475Smrg while (last > 0) { 144956cc18dSsnj if ((ld->attribs[last - 1] & CHARDRAWN) == 0) 145d522f475Smrg last--; 146d522f475Smrg else 147d522f475Smrg break; 148d522f475Smrg } 1492e4f8982Smrg 150d522f475Smrg if (last) { 1512e4f8982Smrg int col; 1522e4f8982Smrg int cs = CSET_IN; 1532e4f8982Smrg int last_cs = CSET_IN; 1542e4f8982Smrg 15520d2c4d2Smrg if (p->print_attributes) { 156956cc18dSsnj send_CharSet(xw, ld); 157956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 158d522f475Smrg } 159d522f475Smrg for (col = 0; col < last; col++) { 1602e4f8982Smrg IAttr attr = 0; 1612e4f8982Smrg unsigned ch = ld->charData[col]; 162d522f475Smrg#if OPT_PRINT_COLORS 163d522f475Smrg if (screen->colorMode) { 16420d2c4d2Smrg if (p->print_attributes > 1) { 165956cc18dSsnj fg = (ld->attribs[col] & FG_COLOR) 166956cc18dSsnj ? extract_fg(xw, ColorOf(ld, col), ld->attribs[col]) 167d522f475Smrg : NO_COLOR; 168956cc18dSsnj bg = (ld->attribs[col] & BG_COLOR) 169956cc18dSsnj ? extract_bg(xw, ColorOf(ld, col), ld->attribs[col]) 170d522f475Smrg : NO_COLOR; 171d522f475Smrg } 172d522f475Smrg } 173d522f475Smrg#endif 174f2e35a3aSmrg if ((((ld->attribs[col] & ATTRIBUTES) != attr) 175d522f475Smrg#if OPT_PRINT_COLORS 176d522f475Smrg || (last_fg != fg) || (last_bg != bg) 177d522f475Smrg#endif 178d522f475Smrg ) 179d522f475Smrg && ch) { 180f2e35a3aSmrg attr = (IAttr) (ld->attribs[col] & ATTRIBUTES); 18120d2c4d2Smrg#if OPT_PRINT_COLORS 182d522f475Smrg last_fg = fg; 183d522f475Smrg last_bg = bg; 18420d2c4d2Smrg#endif 18520d2c4d2Smrg if (p->print_attributes) 186f2e35a3aSmrg send_SGR(xw, attr, (unsigned) fg, (unsigned) bg); 187d522f475Smrg } 188d522f475Smrg 189d522f475Smrg if (ch == 0) 190d522f475Smrg ch = ' '; 191d522f475Smrg 192d522f475Smrg#if OPT_WIDE_CHARS 193d522f475Smrg if (screen->utf8_mode) 194d522f475Smrg cs = CSET_IN; 195d522f475Smrg else 196d522f475Smrg#endif 197d522f475Smrg cs = (ch >= ' ' && ch != ANSI_DEL) ? CSET_IN : CSET_OUT; 198d522f475Smrg if (last_cs != cs) { 19920d2c4d2Smrg if (p->print_attributes) { 200956cc18dSsnj charToPrinter(xw, 201956cc18dSsnj (unsigned) ((cs == CSET_OUT) 202d522f475Smrg ? SHIFT_OUT 203d522f475Smrg : SHIFT_IN)); 204d522f475Smrg } 205d522f475Smrg last_cs = cs; 206d522f475Smrg } 207d522f475Smrg 208d522f475Smrg /* FIXME: we shouldn't have to map back from the 209d522f475Smrg * alternate character set, except that the 210d522f475Smrg * corresponding charset information is not encoded 211d522f475Smrg * into the CSETS array. 212d522f475Smrg */ 213956cc18dSsnj charToPrinter(xw, 214956cc18dSsnj ((cs == CSET_OUT) 215d522f475Smrg ? (ch == ANSI_DEL ? 0x5f : (ch + 0x5f)) 216d522f475Smrg : ch)); 217d522f475Smrg if_OPT_WIDE_CHARS(screen, { 218956cc18dSsnj size_t off; 219956cc18dSsnj for_each_combData(off, ld) { 220956cc18dSsnj ch = ld->combData[off][col]; 221956cc18dSsnj if (ch == 0) 222d522f475Smrg break; 223956cc18dSsnj charToPrinter(xw, ch); 224d522f475Smrg } 225d522f475Smrg }); 226d522f475Smrg } 22720d2c4d2Smrg if (p->print_attributes) { 228956cc18dSsnj send_SGR(xw, 0, NO_COLOR, NO_COLOR); 229d522f475Smrg if (cs != CSET_IN) 230956cc18dSsnj charToPrinter(xw, SHIFT_IN); 231d522f475Smrg } 232d522f475Smrg } 23320d2c4d2Smrg 23420d2c4d2Smrg /* finish line (protocol for attributes needs a CR */ 23520d2c4d2Smrg if (p->print_attributes) 236956cc18dSsnj charToPrinter(xw, '\r'); 23720d2c4d2Smrg 23820d2c4d2Smrg if (chr && !(p->printer_newline)) { 23920d2c4d2Smrg if (LineTstWrapped(ld)) 24020d2c4d2Smrg chr = '\0'; 24120d2c4d2Smrg } 24220d2c4d2Smrg 24320d2c4d2Smrg if (chr) 24420d2c4d2Smrg charToPrinter(xw, chr); 245956cc18dSsnj 246956cc18dSsnj return; 247d522f475Smrg} 248d522f475Smrg 24920d2c4d2Smrg#define PrintNewLine() (unsigned) (((top < bot) || p->printer_newline) ? '\n' : '\0') 25020d2c4d2Smrg 251e39b573cSmrgstatic void 252894e0ac8SmrgprintLines(XtermWidget xw, int top, int bot, PrinterFlags *p) 253e39b573cSmrg{ 254e39b573cSmrg TRACE(("printLines, rows %d..%d\n", top, bot)); 255e39b573cSmrg while (top <= bot) { 256e39b573cSmrg printLine(xw, top, PrintNewLine(), p); 257e39b573cSmrg ++top; 258e39b573cSmrg } 259e39b573cSmrg} 260e39b573cSmrg 261d522f475Smrgvoid 262894e0ac8SmrgxtermPrintScreen(XtermWidget xw, Bool use_DECPEX, PrinterFlags *p) 263d522f475Smrg{ 264956cc18dSsnj if (XtIsRealized((Widget) xw)) { 265956cc18dSsnj TScreen *screen = TScreenOf(xw); 26620d2c4d2Smrg Bool extent = (use_DECPEX && p->printer_extent); 267e39b573cSmrg Boolean was_open = SPS.isOpen; 268d522f475Smrg 269e39b573cSmrg printLines(xw, 270e39b573cSmrg extent ? 0 : screen->top_marg, 271e39b573cSmrg extent ? screen->max_row : screen->bot_marg, 272e39b573cSmrg p); 27320d2c4d2Smrg if (p->printer_formfeed) 274956cc18dSsnj charToPrinter(xw, '\f'); 275d522f475Smrg 276e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 277956cc18dSsnj closePrinter(xw); 278d522f475Smrg } 279d522f475Smrg } else { 28020d2c4d2Smrg Bell(xw, XkbBI_MinorError, 0); 281d522f475Smrg } 282d522f475Smrg} 283d522f475Smrg 284d522f475Smrg/* 285e39b573cSmrg * If p->print_everything is zero, use this behavior: 286d522f475Smrg * If the alternate screen is active, we'll print only that. Otherwise, print 287d522f475Smrg * the normal screen plus all scrolled-back lines. The distinction is made 288d522f475Smrg * because the normal screen's buffer is part of the overall scrollback buffer. 289e39b573cSmrg * 290e39b573cSmrg * Otherwise, decode bits: 291e39b573cSmrg * 1 = current screen 292e39b573cSmrg * 2 = normal screen 293e39b573cSmrg * 4 = alternate screen 294e39b573cSmrg * 8 = saved lines 295d522f475Smrg */ 296956cc18dSsnjvoid 297894e0ac8SmrgxtermPrintEverything(XtermWidget xw, PrinterFlags *p) 298d522f475Smrg{ 299956cc18dSsnj TScreen *screen = TScreenOf(xw); 300e39b573cSmrg Boolean was_open = SPS.isOpen; 301e39b573cSmrg int save_which = screen->whichBuf; 302d522f475Smrg 3030bd37d32Smrg DEBUG_MSG("xtermPrintEverything\n"); 3042e4f8982Smrg 305e39b573cSmrg if (p->print_everything) { 3062e4f8982Smrg int done_which = 0; 3072e4f8982Smrg 308e39b573cSmrg if (p->print_everything & 8) { 309e39b573cSmrg printLines(xw, -screen->savedlines, -(screen->topline + 1), p); 310e39b573cSmrg } 311e39b573cSmrg if (p->print_everything & 4) { 3125307cd1aSmrg SwitchBufPtrs(xw, 1); 313e39b573cSmrg done_which |= 2; 314e39b573cSmrg printLines(xw, 0, screen->max_row, p); 3155307cd1aSmrg SwitchBufPtrs(xw, save_which); 316e39b573cSmrg } 317e39b573cSmrg if (p->print_everything & 2) { 3185307cd1aSmrg SwitchBufPtrs(xw, 0); 319e39b573cSmrg done_which |= 1; 320e39b573cSmrg printLines(xw, 0, screen->max_row, p); 3215307cd1aSmrg SwitchBufPtrs(xw, save_which); 322e39b573cSmrg } 323e39b573cSmrg if (p->print_everything & 1) { 324e39b573cSmrg if (!(done_which & (1 << screen->whichBuf))) { 325e39b573cSmrg printLines(xw, 0, screen->max_row, p); 326e39b573cSmrg } 327e39b573cSmrg } 328e39b573cSmrg } else { 329e39b573cSmrg int top = 0; 330e39b573cSmrg int bot = screen->max_row; 331e39b573cSmrg if (!screen->whichBuf) { 332e39b573cSmrg top = -screen->savedlines - screen->topline; 333e39b573cSmrg bot -= screen->topline; 334e39b573cSmrg } 335e39b573cSmrg printLines(xw, top, bot, p); 33620d2c4d2Smrg } 33720d2c4d2Smrg if (p->printer_formfeed) 338956cc18dSsnj charToPrinter(xw, '\f'); 339d522f475Smrg 340e39b573cSmrg if (!was_open || SPS.printer_autoclose) { 341956cc18dSsnj closePrinter(xw); 342d522f475Smrg } 343d522f475Smrg} 344d522f475Smrg 345d522f475Smrgstatic void 346894e0ac8Smrgsend_CharSet(XtermWidget xw, LineData *ld) 347d522f475Smrg{ 348d522f475Smrg#if OPT_DEC_CHRSET 3495104ee6eSmrg const char *msg = NULL; 350d522f475Smrg 351956cc18dSsnj switch (GetLineDblCS(ld)) { 352d522f475Smrg case CSET_SWL: 353d522f475Smrg msg = "\033#5"; 354d522f475Smrg break; 355d522f475Smrg case CSET_DHL_TOP: 356d522f475Smrg msg = "\033#3"; 357d522f475Smrg break; 358d522f475Smrg case CSET_DHL_BOT: 359d522f475Smrg msg = "\033#4"; 360d522f475Smrg break; 361d522f475Smrg case CSET_DWL: 362d522f475Smrg msg = "\033#6"; 363d522f475Smrg break; 364d522f475Smrg } 3655104ee6eSmrg if (msg != NULL) 366956cc18dSsnj stringToPrinter(xw, msg); 367d522f475Smrg#else 368956cc18dSsnj (void) xw; 369956cc18dSsnj (void) ld; 370d522f475Smrg#endif /* OPT_DEC_CHRSET */ 371d522f475Smrg} 372d522f475Smrg 373d522f475Smrgstatic void 374956cc18dSsnjsend_SGR(XtermWidget xw, unsigned attr, unsigned fg, unsigned bg) 375d522f475Smrg{ 376d522f475Smrg char msg[80]; 377e39b573cSmrg 378f2e35a3aSmrg#if OPT_ISO_COLORS && OPT_PC_COLORS 379f2e35a3aSmrg if ((attr & FG_COLOR) && (fg != NO_COLOR)) { 38020d2c4d2Smrg if (TScreenOf(xw)->boldColors 381d522f475Smrg && fg > 8 38204b94745Smrg && fg < 16 383d522f475Smrg && (attr & BOLD) != 0) 384d522f475Smrg fg -= 8; 385d522f475Smrg } 386d522f475Smrg#endif 387f2e35a3aSmrg strcpy(msg, "\033["); 388f2e35a3aSmrg xtermFormatSGR(xw, msg + strlen(msg), attr, (int) fg, (int) bg); 389d522f475Smrg strcat(msg, "m"); 390956cc18dSsnj stringToPrinter(xw, msg); 391d522f475Smrg} 392d522f475Smrg 393d522f475Smrg/* 394d522f475Smrg * This implementation only knows how to write to a pipe. 395d522f475Smrg */ 396d522f475Smrgstatic void 397956cc18dSsnjcharToPrinter(XtermWidget xw, unsigned chr) 398d522f475Smrg{ 399956cc18dSsnj TScreen *screen = TScreenOf(xw); 400d522f475Smrg 4015104ee6eSmrg if (!screen->print_rawchars) { 40204b94745Smrg#if OPT_WIDE_CHARS 4035104ee6eSmrg if (screen->wide_chars && screen->utf8_mode) { 4045104ee6eSmrg if (chr == UCS_REPL) { 4055104ee6eSmrg stringToPrinter(xw, screen->default_string); 4065104ee6eSmrg return; 4075104ee6eSmrg } 40804b94745Smrg } 40904b94745Smrg#endif 4105104ee6eSmrg if (is_NON_CHAR(chr)) 4115104ee6eSmrg return; 4125104ee6eSmrg } 41304b94745Smrg 414f2e35a3aSmrg if (!SPS.isOpen && (SPS.toFile || xtermHasPrinter(xw))) { 415e39b573cSmrg switch (SPS.toFile) { 416e39b573cSmrg /* 417e39b573cSmrg * write to a pipe. 418e39b573cSmrg */ 419e39b573cSmrg case False: 420e39b573cSmrg { 421e39b573cSmrg int my_pipe[2]; 422e39b573cSmrg pid_t my_pid; 423e39b573cSmrg 424e39b573cSmrg if (pipe(my_pipe)) 425e39b573cSmrg SysError(ERROR_FORK); 426e39b573cSmrg if ((my_pid = fork()) < 0) 427e39b573cSmrg SysError(ERROR_FORK); 428e39b573cSmrg 429e39b573cSmrg if (my_pid == 0) { 4300bd37d32Smrg DEBUG_MSG("charToPrinter: subprocess for printer\n"); 431e39b573cSmrg TRACE_CLOSE(); 432e39b573cSmrg close(my_pipe[1]); /* printer is silent */ 433e39b573cSmrg close(screen->respond); 434e39b573cSmrg 435e39b573cSmrg close(fileno(stdout)); 436e39b573cSmrg dup2(fileno(stderr), 1); 437e39b573cSmrg 438e39b573cSmrg if (fileno(stderr) != 2) { 439e39b573cSmrg dup2(fileno(stderr), 2); 440e39b573cSmrg close(fileno(stderr)); 441e39b573cSmrg } 442e39b573cSmrg 443e39b573cSmrg /* don't want privileges! */ 444e39b573cSmrg if (xtermResetIds(screen) < 0) 44504b94745Smrg exit(ERROR_MISC); 446e39b573cSmrg 447e39b573cSmrg SPS.fp = popen(SPS.printer_command, "w"); 4485104ee6eSmrg if (SPS.fp != NULL) { 4492e4f8982Smrg FILE *input; 4500bd37d32Smrg DEBUG_MSG("charToPrinter: opened pipe to printer\n"); 4515104ee6eSmrg if ((input = fdopen(my_pipe[0], "r")) != NULL) { 452913cc679Smrg clearerr(input); 453913cc679Smrg 454913cc679Smrg for (;;) { 455913cc679Smrg int c; 456913cc679Smrg 457913cc679Smrg if (ferror(input)) { 458913cc679Smrg DEBUG_MSG("charToPrinter: break on ferror\n"); 459913cc679Smrg break; 460913cc679Smrg } else if (feof(input)) { 461913cc679Smrg DEBUG_MSG("charToPrinter: break on feof\n"); 462913cc679Smrg break; 463913cc679Smrg } else if ((c = fgetc(input)) == EOF) { 464913cc679Smrg DEBUG_MSG("charToPrinter: break on EOF\n"); 465913cc679Smrg break; 466913cc679Smrg } 467913cc679Smrg fputc(c, SPS.fp); 468913cc679Smrg if (isForm(c)) 469913cc679Smrg fflush(SPS.fp); 4700bd37d32Smrg } 4710bd37d32Smrg } 4720bd37d32Smrg DEBUG_MSG("charToPrinter: calling pclose\n"); 4730bd37d32Smrg pclose(SPS.fp); 474913cc679Smrg if (input) 475913cc679Smrg fclose(input); 476e39b573cSmrg } 477e39b573cSmrg exit(0); 478e39b573cSmrg } else { 479e39b573cSmrg close(my_pipe[0]); /* won't read from printer */ 4805104ee6eSmrg if ((SPS.fp = fdopen(my_pipe[1], "w")) != NULL) { 4810bd37d32Smrg DEBUG_MSG("charToPrinter: opened printer in parent\n"); 4820bd37d32Smrg TRACE(("opened printer from pid %d/%d\n", 4830bd37d32Smrg (int) getpid(), (int) my_pid)); 4840bd37d32Smrg } else { 4850bd37d32Smrg TRACE(("failed to open printer:%s\n", strerror(errno))); 4860bd37d32Smrg DEBUG_MSG("charToPrinter: could not open in parent\n"); 4870bd37d32Smrg } 488e39b573cSmrg } 489d522f475Smrg } 490e39b573cSmrg break; 491e39b573cSmrg case True: 492e39b573cSmrg TRACE(("opening \"%s\" as printer output\n", SPS.printer_command)); 493e39b573cSmrg SPS.fp = fopen(SPS.printer_command, "w"); 494e39b573cSmrg break; 495e39b573cSmrg } 496e39b573cSmrg SPS.isOpen = True; 497d522f475Smrg } 4985104ee6eSmrg if (SPS.fp != NULL) { 499d522f475Smrg#if OPT_WIDE_CHARS 500d522f475Smrg if (chr > 127) { 501d522f475Smrg Char temp[10]; 502d522f475Smrg *convertToUTF8(temp, chr) = 0; 503e39b573cSmrg fputs((char *) temp, SPS.fp); 504d522f475Smrg } else 505d522f475Smrg#endif 506e39b573cSmrg fputc((int) chr, SPS.fp); 507d522f475Smrg if (isForm(chr)) 508e39b573cSmrg fflush(SPS.fp); 509d522f475Smrg } 510d522f475Smrg} 511d522f475Smrg 512d522f475Smrgstatic void 51320d2c4d2SmrgstringToPrinter(XtermWidget xw, const char *str) 514d522f475Smrg{ 515d522f475Smrg while (*str) 516956cc18dSsnj charToPrinter(xw, CharOf(*str++)); 517d522f475Smrg} 518d522f475Smrg 519d522f475Smrg/* 520d522f475Smrg * This module implements the MC (Media Copy) and related printing control 521d522f475Smrg * sequences for VTxxx emulation. This is based on the description in the 522d522f475Smrg * VT330/VT340 Programmer Reference Manual EK-VT3XX-TP-001 (Digital Equipment 523d522f475Smrg * Corp., March 1987). 524d522f475Smrg */ 525d522f475Smrgvoid 526956cc18dSsnjxtermMediaControl(XtermWidget xw, int param, int private_seq) 527d522f475Smrg{ 528d522f475Smrg TRACE(("MediaCopy param=%d, private=%d\n", param, private_seq)); 529d522f475Smrg 530d522f475Smrg if (private_seq) { 531d522f475Smrg switch (param) { 532f2e35a3aSmrg case -1: 533f2e35a3aSmrg case 0: /* VT125 */ 534f2e35a3aSmrg setGraphicsPrintToHost(xw, 0); /* graphics to printer */ 535f2e35a3aSmrg break; 536d522f475Smrg case 1: 537956cc18dSsnj printCursorLine(xw); 538d522f475Smrg break; 539f2e35a3aSmrg case 2: /* VT125 */ 540f2e35a3aSmrg setGraphicsPrintToHost(xw, 1); /* graphics to host */ 541f2e35a3aSmrg break; 542d522f475Smrg case 4: 543f2e35a3aSmrg setPrinterControlMode(xw, 0); /* autoprint disable */ 544d522f475Smrg break; 545d522f475Smrg case 5: 546f2e35a3aSmrg setPrinterControlMode(xw, 1); /* autoprint enable */ 547d522f475Smrg break; 548d522f475Smrg case 10: /* VT320 */ 549f2e35a3aSmrg /* print whole screen, across sessions */ 5505104ee6eSmrg xtermPrintScreen(xw, False, getPrinterFlags(xw, NULL, NULL)); 551d522f475Smrg break; 552d522f475Smrg case 11: /* VT320 */ 553f2e35a3aSmrg /* print all pages in current session */ 5545104ee6eSmrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, NULL)); 555d522f475Smrg break; 556d522f475Smrg } 557d522f475Smrg } else { 558d522f475Smrg switch (param) { 559d522f475Smrg case -1: 560d522f475Smrg case 0: 5615104ee6eSmrg xtermPrintScreen(xw, True, getPrinterFlags(xw, NULL, NULL)); 562d522f475Smrg break; 563d522f475Smrg case 4: 564f2e35a3aSmrg setPrinterControlMode(xw, 0); /* printer controller mode off */ 565d522f475Smrg break; 566d522f475Smrg case 5: 567f2e35a3aSmrg setPrinterControlMode(xw, 2); /* printer controller mode on */ 568d522f475Smrg break; 5692e4f8982Smrg#if OPT_SCREEN_DUMPS 5702e4f8982Smrg case 10: 5712e4f8982Smrg xtermDumpHtml(xw); 5722e4f8982Smrg break; 5732e4f8982Smrg case 11: 5742e4f8982Smrg xtermDumpSvg(xw); 5752e4f8982Smrg break; 5762e4f8982Smrg#endif 577d522f475Smrg } 578d522f475Smrg } 579d522f475Smrg} 580d522f475Smrg 581d522f475Smrg/* 582d522f475Smrg * When in autoprint mode, the printer prints a line from the screen when you 583d522f475Smrg * move the cursor off that line with an LF, FF, or VT character, or an 584d522f475Smrg * autowrap occurs. The printed line ends with a CR and the character (LF, FF 585d522f475Smrg * or VT) that moved the cursor off the previous line. 586d522f475Smrg */ 587d522f475Smrgvoid 588956cc18dSsnjxtermAutoPrint(XtermWidget xw, unsigned chr) 589d522f475Smrg{ 590956cc18dSsnj TScreen *screen = TScreenOf(xw); 591d522f475Smrg 592e39b573cSmrg if (SPS.printer_controlmode == 1) { 593d522f475Smrg TRACE(("AutoPrint %d\n", chr)); 5945104ee6eSmrg printLine(xw, screen->cursorp.row, chr, getPrinterFlags(xw, NULL, NULL)); 5955104ee6eSmrg if (SPS.fp != NULL) 596e39b573cSmrg fflush(SPS.fp); 597d522f475Smrg } 598d522f475Smrg} 599d522f475Smrg 600d522f475Smrg/* 601d522f475Smrg * When in printer controller mode, the terminal sends received characters to 602d522f475Smrg * the printer without displaying them on the screen. The terminal sends all 603d522f475Smrg * characters and control sequences to the printer, except NUL, XON, XOFF, and 604d522f475Smrg * the printer controller sequences. 605d522f475Smrg * 606d522f475Smrg * This function eats characters, returning 0 as long as it must buffer or 607d522f475Smrg * divert to the printer. We're only invoked here when in printer controller 608d522f475Smrg * mode, and handle the exit from that mode. 609d522f475Smrg */ 610d522f475Smrg#define LB '[' 611d522f475Smrg 612d522f475Smrgint 613956cc18dSsnjxtermPrinterControl(XtermWidget xw, int chr) 614d522f475Smrg{ 615956cc18dSsnj TScreen *screen = TScreenOf(xw); 616d522f475Smrg /* *INDENT-OFF* */ 617e39b573cSmrg static const struct { 618e39b573cSmrg const Char seq[5]; 619d522f475Smrg int active; 620d522f475Smrg } tbl[] = { 621d522f475Smrg { { ANSI_CSI, '5', 'i' }, 2 }, 622d522f475Smrg { { ANSI_CSI, '4', 'i' }, 0 }, 623d522f475Smrg { { ANSI_ESC, LB, '5', 'i' }, 2 }, 624d522f475Smrg { { ANSI_ESC, LB, '4', 'i' }, 0 }, 625d522f475Smrg }; 626d522f475Smrg /* *INDENT-ON* */ 627d522f475Smrg 628d522f475Smrg static Char bfr[10]; 629d522f475Smrg static size_t length; 630d522f475Smrg size_t n; 631d522f475Smrg 632d522f475Smrg TRACE(("In printer:%04X\n", chr)); 633d522f475Smrg 634d522f475Smrg switch (chr) { 635d522f475Smrg case 0: 636d522f475Smrg case CTRL('Q'): 637d522f475Smrg case CTRL('S'): 638d522f475Smrg return 0; /* ignored by application */ 639d522f475Smrg 640d522f475Smrg case ANSI_CSI: 641d522f475Smrg case ANSI_ESC: 642d522f475Smrg case '[': 643d522f475Smrg case '4': 644d522f475Smrg case '5': 645d522f475Smrg case 'i': 6464e40088cSchristos bfr[length++] = CharOf(chr); 647d522f475Smrg for (n = 0; n < sizeof(tbl) / sizeof(tbl[0]); n++) { 648d522f475Smrg size_t len = Strlen(tbl[n].seq); 649d522f475Smrg 650d522f475Smrg if (length == len 651d522f475Smrg && Strcmp(bfr, tbl[n].seq) == 0) { 652956cc18dSsnj setPrinterControlMode(xw, tbl[n].active); 653e39b573cSmrg if (SPS.printer_autoclose 654e39b573cSmrg && SPS.printer_controlmode == 0) 655956cc18dSsnj closePrinter(xw); 656d522f475Smrg length = 0; 657d522f475Smrg return 0; 658d522f475Smrg } else if (len > length 659d522f475Smrg && Strncmp(bfr, tbl[n].seq, length) == 0) { 660d522f475Smrg return 0; 661d522f475Smrg } 662d522f475Smrg } 663d522f475Smrg length--; 664d522f475Smrg 665d522f475Smrg /* FALLTHRU */ 666d522f475Smrg 667d522f475Smrg default: 668d522f475Smrg for (n = 0; n < length; n++) 669956cc18dSsnj charToPrinter(xw, bfr[n]); 6704e40088cSchristos bfr[0] = CharOf(chr); 671d522f475Smrg length = 1; 672d522f475Smrg return 0; 673d522f475Smrg } 674d522f475Smrg} 675d522f475Smrg 676d522f475Smrg/* 677d522f475Smrg * If there is no printer command, we will ignore printer controls. 6780bd37d32Smrg * 6790bd37d32Smrg * If we do have a printer command, we still have to verify that it will 6800bd37d32Smrg * (perhaps) work if we pass it to popen(). At a minimum, the program 6810bd37d32Smrg * must exist and be executable. If not, warn and disable the feature. 682d522f475Smrg */ 683d522f475SmrgBool 684956cc18dSsnjxtermHasPrinter(XtermWidget xw) 685d522f475Smrg{ 686956cc18dSsnj TScreen *screen = TScreenOf(xw); 6870bd37d32Smrg Bool result = SPS.printer_checked; 6880bd37d32Smrg 6890bd37d32Smrg if (strlen(SPS.printer_command) != 0 && !result) { 6900bd37d32Smrg char **argv = x_splitargs(SPS.printer_command); 6910bd37d32Smrg if (argv) { 6920bd37d32Smrg if (argv[0]) { 6930bd37d32Smrg char *myShell = xtermFindShell(argv[0], False); 6945104ee6eSmrg if (myShell == NULL) { 6950bd37d32Smrg xtermWarning("No program found for printerCommand: %s\n", SPS.printer_command); 6960bd37d32Smrg SPS.printer_command = x_strdup(""); 6970bd37d32Smrg } else { 6980bd37d32Smrg free(myShell); 6990bd37d32Smrg SPS.printer_checked = True; 7000bd37d32Smrg result = True; 7010bd37d32Smrg } 7020bd37d32Smrg } 7030bd37d32Smrg x_freeargs(argv); 7040bd37d32Smrg } 7050bd37d32Smrg TRACE(("xtermHasPrinter:%d\n", result)); 7060bd37d32Smrg } 707d522f475Smrg 7080bd37d32Smrg return result; 709d522f475Smrg} 710d522f475Smrg 711f2e35a3aSmrg#if OPT_PRINT_GRAPHICS 712f2e35a3aSmrgstatic void 713f2e35a3aSmrgsetGraphicsPrintToHost(XtermWidget xw, int enabled) 714f2e35a3aSmrg{ 715f2e35a3aSmrg TScreen *screen = TScreenOf(xw); 716f2e35a3aSmrg 717f2e35a3aSmrg TRACE(("graphics print to host enabled=%d\n", enabled)); 718f2e35a3aSmrg screen->graphics_print_to_host = (Boolean) enabled; 719f2e35a3aSmrg} 720f2e35a3aSmrg#endif 721f2e35a3aSmrg 722d522f475Smrg#define showPrinterControlMode(mode) \ 723d522f475Smrg (((mode) == 0) \ 724d522f475Smrg ? "normal" \ 725d522f475Smrg : ((mode) == 1 \ 726d522f475Smrg ? "autoprint" \ 727d522f475Smrg : "printer controller")) 728d522f475Smrg 729d522f475Smrgvoid 730956cc18dSsnjsetPrinterControlMode(XtermWidget xw, int mode) 731d522f475Smrg{ 73220d2c4d2Smrg TScreen *screen = TScreenOf(xw); 73320d2c4d2Smrg 734956cc18dSsnj if (xtermHasPrinter(xw) 735e39b573cSmrg && SPS.printer_controlmode != mode) { 736d522f475Smrg TRACE(("%s %s mode\n", 737d522f475Smrg (mode 738d522f475Smrg ? "set" 739d522f475Smrg : "reset"), 740d522f475Smrg (mode 741d522f475Smrg ? showPrinterControlMode(mode) 742e39b573cSmrg : showPrinterControlMode(SPS.printer_controlmode)))); 743e39b573cSmrg SPS.printer_controlmode = mode; 744d522f475Smrg update_print_redir(); 745d522f475Smrg } 746d522f475Smrg} 74720d2c4d2Smrg 74820d2c4d2SmrgPrinterFlags * 749894e0ac8SmrggetPrinterFlags(XtermWidget xw, String *params, Cardinal *param_count) 75020d2c4d2Smrg{ 75120d2c4d2Smrg /* *INDENT-OFF* */ 75220d2c4d2Smrg static const struct { 75320d2c4d2Smrg const char *name; 75420d2c4d2Smrg unsigned offset; 75520d2c4d2Smrg int value; 75620d2c4d2Smrg } table[] = { 75720d2c4d2Smrg { "noFormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 0 }, 75820d2c4d2Smrg { "FormFeed", XtOffsetOf(PrinterFlags, printer_formfeed), 1 }, 75920d2c4d2Smrg { "noNewLine", XtOffsetOf(PrinterFlags, printer_newline), 0 }, 76020d2c4d2Smrg { "NewLine", XtOffsetOf(PrinterFlags, printer_newline), 1 }, 76120d2c4d2Smrg { "noAttrs", XtOffsetOf(PrinterFlags, print_attributes), 0 }, 76220d2c4d2Smrg { "monoAttrs", XtOffsetOf(PrinterFlags, print_attributes), 1 }, 76320d2c4d2Smrg { "colorAttrs", XtOffsetOf(PrinterFlags, print_attributes), 2 }, 76420d2c4d2Smrg }; 76520d2c4d2Smrg /* *INDENT-ON* */ 76620d2c4d2Smrg 76720d2c4d2Smrg TScreen *screen = TScreenOf(xw); 76820d2c4d2Smrg PrinterFlags *result = &(screen->printer_flags); 76920d2c4d2Smrg 77020d2c4d2Smrg TRACE(("getPrinterFlags %d params\n", param_count ? *param_count : 0)); 77120d2c4d2Smrg 772e39b573cSmrg result->printer_extent = SPS.printer_extent; 773e39b573cSmrg result->printer_formfeed = SPS.printer_formfeed; 774e39b573cSmrg result->printer_newline = SPS.printer_newline; 775e39b573cSmrg result->print_attributes = SPS.print_attributes; 776e39b573cSmrg result->print_everything = SPS.print_everything; 77720d2c4d2Smrg 7785104ee6eSmrg if (param_count != NULL && *param_count != 0) { 77920d2c4d2Smrg Cardinal j; 78020d2c4d2Smrg unsigned k; 78120d2c4d2Smrg for (j = 0; j < *param_count; ++j) { 78220d2c4d2Smrg TRACE(("param%d:%s\n", j, params[j])); 78320d2c4d2Smrg for (k = 0; k < XtNumber(table); ++k) { 78420d2c4d2Smrg if (!x_strcasecmp(params[j], table[k].name)) { 785a1f3da82Smrg int *ptr = (int *) (void *) ((char *) result + table[k].offset); 78620d2c4d2Smrg TRACE(("...PrinterFlags(%s) %d->%d\n", 78720d2c4d2Smrg table[k].name, 78820d2c4d2Smrg *ptr, 78920d2c4d2Smrg table[k].value)); 79020d2c4d2Smrg *ptr = table[k].value; 79120d2c4d2Smrg break; 79220d2c4d2Smrg } 79320d2c4d2Smrg } 79420d2c4d2Smrg } 79520d2c4d2Smrg } 79620d2c4d2Smrg 79720d2c4d2Smrg return result; 79820d2c4d2Smrg} 799e39b573cSmrg 800e39b573cSmrg/* 801e39b573cSmrg * Print a timestamped copy of everything. 802e39b573cSmrg */ 803e39b573cSmrgvoid 804e39b573cSmrgxtermPrintImmediately(XtermWidget xw, String filename, int opts, int attrs) 805e39b573cSmrg{ 806e39b573cSmrg TScreen *screen = TScreenOf(xw); 807e39b573cSmrg PrinterState save_state = screen->printer_state; 808e39b573cSmrg char *my_filename = malloc(TIMESTAMP_LEN + strlen(filename)); 809e39b573cSmrg 8105104ee6eSmrg if (my_filename != NULL) { 811913cc679Smrg mode_t save_umask = umask(0177); 812e39b573cSmrg 813e39b573cSmrg timestamp_filename(my_filename, filename); 8145104ee6eSmrg SPS.fp = NULL; 815e39b573cSmrg SPS.isOpen = False; 816e39b573cSmrg SPS.toFile = True; 817e39b573cSmrg SPS.printer_command = my_filename; 818e39b573cSmrg SPS.printer_autoclose = True; 819e39b573cSmrg SPS.printer_formfeed = False; 820e39b573cSmrg SPS.printer_newline = True; 821e39b573cSmrg SPS.print_attributes = attrs; 822e39b573cSmrg SPS.print_everything = opts; 8235104ee6eSmrg xtermPrintEverything(xw, getPrinterFlags(xw, NULL, NULL)); 824e39b573cSmrg 825e39b573cSmrg umask(save_umask); 826e39b573cSmrg screen->printer_state = save_state; 827ad37e533Smrg free(my_filename); 828e39b573cSmrg } 829e39b573cSmrg} 830e39b573cSmrg 831e39b573cSmrgvoid 832e39b573cSmrgxtermPrintOnXError(XtermWidget xw, int n) 833e39b573cSmrg{ 834e39b573cSmrg#if OPT_PRINT_ON_EXIT 835e39b573cSmrg /* 836e39b573cSmrg * The user may have requested that the contents of the screen will be 837e39b573cSmrg * written to a file if an X error occurs. 838e39b573cSmrg */ 839e39b573cSmrg if (TScreenOf(xw)->write_error && !IsEmpty(resource.printFileOnXError)) { 840e39b573cSmrg Boolean printIt = False; 841e39b573cSmrg 842e39b573cSmrg switch (n) { 843e39b573cSmrg case ERROR_XERROR: 844e39b573cSmrg /* FALLTHRU */ 845e39b573cSmrg case ERROR_XIOERROR: 846e39b573cSmrg /* FALLTHRU */ 847e39b573cSmrg case ERROR_ICEERROR: 848e39b573cSmrg printIt = True; 849e39b573cSmrg break; 850e39b573cSmrg } 851e39b573cSmrg 852e39b573cSmrg if (printIt) { 853e39b573cSmrg xtermPrintImmediately(xw, 854e39b573cSmrg resource.printFileOnXError, 855e39b573cSmrg resource.printOptsOnXError, 856e39b573cSmrg resource.printModeOnXError); 857e39b573cSmrg } 858e39b573cSmrg } 859e39b573cSmrg#else 860e39b573cSmrg (void) xw; 861e39b573cSmrg (void) n; 862e39b573cSmrg#endif 863e39b573cSmrg} 864